libata-scsi.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,571 行 · 第 1/3 页
C
1,571 行
* 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){ const u8 page[] = { 0x1, /* page code */ 0xa, /* page length */ (1 << 7) | (1 << 6), /* note auto r/w reallocation */ 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 9 zeroes */ }; ata_msense_push(ptr_io, last, page, sizeof(page)); return sizeof(page);}/** * ata_scsiop_mode_sense - Simulate MODE SENSE 6, 10 commands * @args: Port / device / SCSI command of interest. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @buflen: Response buffer length. * * Simulate MODE SENSE commands. * * LOCKING: * spin_lock_irqsave(host_set lock) */unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen){ u8 *scsicmd = args->cmd->cmnd, *p, *last; struct ata_device *dev = args->dev; unsigned int page_control, six_byte, output_len; VPRINTK("ENTER\n"); six_byte = (scsicmd[0] == MODE_SENSE); /* we only support saved and current values (which we treat * in the same manner) */ page_control = scsicmd[2] >> 6; if ((page_control != 0) && (page_control != 3)) return 1; if (six_byte) output_len = 4; else output_len = 8; p = rbuf + output_len; last = rbuf + buflen - 1; switch(scsicmd[2] & 0x3f) { case 0x01: /* r/w error recovery */ output_len += ata_msense_rw_recovery(&p, last); break; case 0x08: /* caching */ output_len += ata_msense_caching(dev, &p, last); break; case 0x0a: { /* control mode */ output_len += ata_msense_ctl_mode(&p, last); break; } case 0x3f: /* all pages */ output_len += ata_msense_rw_recovery(&p, last); output_len += ata_msense_caching(dev, &p, last); output_len += ata_msense_ctl_mode(&p, last); break; default: /* invalid page code */ return 1; } if (six_byte) { output_len--; rbuf[0] = output_len; } else { output_len -= 2; rbuf[0] = output_len >> 8; rbuf[1] = output_len; } return 0;}/** * ata_scsiop_read_cap - Simulate READ CAPACITY[ 16] commands * @args: Port / device / SCSI command of interest. * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @buflen: Response buffer length. * * Simulate READ CAPACITY commands. * * LOCKING: * spin_lock_irqsave(host_set lock) */unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen){ u64 n_sectors = args->dev->n_sectors; u32 tmp; VPRINTK("ENTER\n"); n_sectors--; /* ATA TotalUserSectors - 1 */ tmp = n_sectors; /* note: truncates, if lba48 */ if (args->cmd->cmnd[0] == READ_CAPACITY) { /* sector count, 32-bit */ rbuf[0] = tmp >> (8 * 3); rbuf[1] = tmp >> (8 * 2); rbuf[2] = tmp >> (8 * 1); rbuf[3] = tmp; /* sector size */ tmp = ATA_SECT_SIZE; rbuf[6] = tmp >> 8; rbuf[7] = tmp; } else { /* sector count, 64-bit */ rbuf[2] = n_sectors >> (8 * 7); rbuf[3] = n_sectors >> (8 * 6); rbuf[4] = n_sectors >> (8 * 5); rbuf[5] = n_sectors >> (8 * 4); rbuf[6] = tmp >> (8 * 3); rbuf[7] = tmp >> (8 * 2); rbuf[8] = tmp >> (8 * 1); rbuf[9] = tmp; /* sector size */ tmp = ATA_SECT_SIZE; rbuf[12] = tmp >> 8; rbuf[13] = tmp; } return 0;}/** * ata_scsiop_report_luns - Simulate REPORT LUNS command * @args: Port / device / 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_set 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_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_set lock) */void ata_scsi_badcmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), u8 asc, u8 ascq){ DPRINTK("ENTER\n"); cmd->result = SAM_STAT_CHECK_CONDITION; cmd->sense_buffer[0] = 0x70; cmd->sense_buffer[2] = ILLEGAL_REQUEST; cmd->sense_buffer[7] = 14 - 8; /* addnl. sense len. FIXME: correct? */ cmd->sense_buffer[12] = asc; cmd->sense_buffer[13] = ascq; done(cmd);}static int atapi_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat){ struct scsi_cmnd *cmd = qc->scsicmd; if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ))) cmd->result = SAM_STAT_CHECK_CONDITION; else { u8 *scsicmd = cmd->cmnd; if (scsicmd[0] == INQUIRY) { u8 *buf = NULL; unsigned int buflen; buflen = ata_scsi_rbuf_get(cmd, &buf); buf[2] = 0x5; buf[3] = (buf[3] & 0xf0) | 2; ata_scsi_rbuf_put(cmd); } cmd->result = SAM_STAT_GOOD; } qc->scsidone(cmd); return 0;}/** * atapi_xlat - Initialize PACKET taskfile * @qc: command structure to be initialized * @scsicmd: SCSI CDB associated with this PACKET command * * LOCKING: * spin_lock_irqsave(host_set lock) * * RETURNS: * Zero on success, non-zero on failure. */static unsigned int atapi_xlat(struct ata_queued_cmd *qc, u8 *scsicmd){ struct scsi_cmnd *cmd = qc->scsicmd; struct ata_device *dev = qc->dev; int using_pio = (dev->flags & ATA_DFLAG_PIO); int nodata = (cmd->sc_data_direction == SCSI_DATA_NONE); memcpy(&qc->cdb, scsicmd, qc->ap->cdb_len); qc->complete_fn = atapi_qc_complete; qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; if (cmd->sc_data_direction == SCSI_DATA_WRITE) { qc->tf.flags |= ATA_TFLAG_WRITE; DPRINTK("direction: write\n"); } qc->tf.command = ATA_CMD_PACKET; /* no data, or PIO data xfer */ if (using_pio || nodata) { if (nodata) qc->tf.protocol = ATA_PROT_ATAPI_NODATA; else qc->tf.protocol = ATA_PROT_ATAPI; qc->tf.lbam = (8 * 1024) & 0xff; qc->tf.lbah = (8 * 1024) >> 8; } /* DMA data xfer */ else { qc->tf.protocol = ATA_PROT_ATAPI_DMA; qc->tf.feature |= ATAPI_PKT_DMA;#ifdef ATAPI_ENABLE_DMADIR /* some SATA bridges need us to indicate data xfer direction */ if (cmd->sc_data_direction != SCSI_DATA_WRITE) qc->tf.feature |= ATAPI_DMADIR;#endif } return 0;}/** * ata_scsi_find_dev - lookup ata_device from scsi_cmnd * @ap: ATA port to which the device is attached * @cmd: SCSI command to be sent to the device * * Given various information provided in struct scsi_cmnd, * map that onto an ATA bus, and using that mapping * determine which ata_device is associated with the * SCSI command to be sent. * * LOCKING: * spin_lock_irqsave(host_set lock) * * RETURNS: * Associated ATA device, or %NULL if not found. */static struct ata_device *ata_scsi_find_dev(struct ata_port *ap, struct scsi_device *scsidev){ struct ata_device *dev; /* skip commands not addressed to targets we simulate */ if (likely(scsidev->id < ATA_MAX_DEVICES)) dev = &ap->device[scsidev->id]; else return NULL; if (unlikely((scsidev->channel != 0) || (scsidev->lun != 0))) return NULL; if (unlikely(!ata_dev_present(dev))) return NULL;#ifndef ATA_ENABLE_ATAPI if (unlikely(dev->class == ATA_DEV_ATAPI)) return NULL;#endif return dev;}/** * ata_get_xlat_func - check if SCSI to ATA translation is possible * @dev: ATA device * @cmd: SCSI command opcode to consider * * Look up the SCSI command given, and determine whether the * SCSI command is to be translated or simulated. * * RETURNS: * Pointer to translation function if possible, %NULL if not. */static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd){ switch (cmd) { case READ_6: case READ_10: case READ_16: case WRITE_6: case WRITE_10: case WRITE_16: return ata_scsi_rw_xlat; case SYNCHRONIZE_CACHE: if (ata_try_flush_cache(dev)) return ata_scsi_flush_xlat; break; case VERIFY: case VERIFY_16: return ata_scsi_verify_xlat; } return NULL;}/** * ata_scsi_dump_cdb - dump SCSI command contents to dmesg * @ap: ATA port to which the command was being sent * @cmd: SCSI command to dump * * Prints the contents of a SCSI command via printk(). */static inline void ata_scsi_dump_cdb(struct ata_port *ap, struct scsi_cmnd *cmd){#ifdef ATA_DEBUG struct scsi_device *scsidev = cmd->device; u8 *scsicmd = cmd->cmnd; DPRINTK("CDB (%u:%d,%d,%d) %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", ap->id, scsidev->channel, scsidev->id, scsidev->lun, scsicmd[0], scsicmd[1], scsicmd[2], scsicmd[3], scsicmd[4], scsicmd[5], scsicmd[6], scsicmd[7], scsicmd[8]);#endif}/** * ata_scsi_queuecmd - Issue SCSI cdb to libata-managed device * @cmd: SCSI command to be sent * @done: Completion function, called when command is complete * * In some cases, this function translates SCSI commands into * ATA taskfiles, and queues the taskfiles to be sent to * hardware. In other cases, this function simulates a * SCSI device by evaluating and responding to certain * SCSI commands. This creates the overall effect of * ATA and ATAPI devices appearing as SCSI devices. * * LOCKING: * Releases scsi-layer-held lock, and obtains host_set lock. * * RETURNS: * Zero. */int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)){ struct ata_port *ap; struct ata_device *dev; struct scsi_device *scsidev = cmd->device; ap = (struct ata_port *) &scsidev->host->hostdata[0]; ata_scsi_dump_cdb(ap, cmd); dev = ata_scsi_find_dev(ap, scsidev); if (unlikely(!dev)) { cmd->result = (DID_BAD_TARGET << 16); done(cmd); goto out_unlock; } if (dev->class == ATA_DEV_ATA) { ata_xlat_func_t xlat_func = ata_get_xlat_func(dev, cmd->cmnd[0]); if (xlat_func) ata_scsi_translate(ap, dev, cmd, done, xlat_func); else ata_scsi_simulate(ap, dev, cmd, done); } else ata_scsi_translate(ap, dev, cmd, done, atapi_xlat);out_unlock: return 0;}/** * ata_scsi_simulate - simulate SCSI command on ATA device * @ap: Port to which ATA device is attached. * @dev: Target device for CDB. * @cmd: SCSI command being sent to device. * @done: SCSI command completion function. * * Interprets and directly executes a select list of SCSI commands * that can be handled internally. * * LOCKING: * spin_lock_irqsave(host_set lock) */static void ata_scsi_simulate(struct ata_port *ap, struct ata_device *dev, struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)){ struct ata_scsi_args args; u8 *scsicmd = cmd->cmnd; args.ap = ap; args.dev = dev; args.cmd = cmd; args.done = done; switch(scsicmd[0]) { /* no-op's, complete with success */ case SYNCHRONIZE_CACHE: case REZERO_UNIT: case SEEK_6: case SEEK_10: case TEST_UNIT_READY: case FORMAT_UNIT: /* FIXME: correct? */ case SEND_DIAGNOSTIC: /* FIXME: correct? */ ata_scsi_rbuf_fill(&args, ata_scsiop_noop); break; case INQUIRY: if (scsicmd[1] & 2) /* is CmdDt set? */ ata_bad_cdb(cmd, done); else if ((scsicmd[1] & 1) == 0) /* is EVPD clear? */ ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std); else if (scsicmd[2] == 0x00) ata_scsi_rbuf_fill(&args, ata_scsiop_inq_00); else if (scsicmd[2] == 0x80) ata_scsi_rbuf_fill(&args, ata_scsiop_inq_80); else if (scsicmd[2] == 0x83) ata_scsi_rbuf_fill(&args, ata_scsiop_inq_83); else ata_bad_cdb(cmd, done); break; case MODE_SENSE: case MODE_SENSE_10: ata_scsi_rbuf_fill(&args, ata_scsiop_mode_sense); break; case MODE_SELECT: /* unconditionally return */ case MODE_SELECT_10: /* bad-field-in-cdb */ ata_bad_cdb(cmd, done); break; case READ_CAPACITY: ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); break; case SERVICE_ACTION_IN: if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16) ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); else ata_bad_cdb(cmd, done); break; case REPORT_LUNS: ata_scsi_rbuf_fill(&args, ata_scsiop_report_luns); break; /* mandantory commands we haven't implemented yet */ case REQUEST_SENSE: /* all other commands */ default: ata_bad_scsiop(cmd, done); break; }}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?