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 + -
显示快捷键?