libata-scsi.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,571 行 · 第 1/3 页

C
1,571
字号
 *	RETURNS: *	Zero on success, non-zero on error. */static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, u8 *scsicmd){	struct ata_taskfile *tf = &qc->tf;	unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48;	tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE;	tf->protocol = qc->dev->xfer_protocol;	tf->device |= ATA_LBA;	if (scsicmd[0] == READ_10 || scsicmd[0] == READ_6 ||	    scsicmd[0] == READ_16) {		tf->command = qc->dev->read_cmd;	} else {		tf->command = qc->dev->write_cmd;		tf->flags |= ATA_TFLAG_WRITE;	}	if (scsicmd[0] == READ_10 || scsicmd[0] == WRITE_10) {		if (lba48) {			tf->hob_nsect = scsicmd[7];			tf->hob_lbal = scsicmd[2];			qc->nsect = ((unsigned int)scsicmd[7] << 8) |					scsicmd[8];		} else {			/* if we don't support LBA48 addressing, the request			 * -may- be too large. */			if ((scsicmd[2] & 0xf0) || scsicmd[7])				return 1;			/* stores LBA27:24 in lower 4 bits of device reg */			tf->device |= scsicmd[2];			qc->nsect = scsicmd[8];		}		tf->nsect = scsicmd[8];		tf->lbal = scsicmd[5];		tf->lbam = scsicmd[4];		tf->lbah = scsicmd[3];		VPRINTK("ten-byte command\n");		return 0;	}	if (scsicmd[0] == READ_6 || scsicmd[0] == WRITE_6) {		qc->nsect = tf->nsect = scsicmd[4];		tf->lbal = scsicmd[3];		tf->lbam = scsicmd[2];		tf->lbah = scsicmd[1] & 0x1f; /* mask out reserved bits */		VPRINTK("six-byte command\n");		return 0;	}	if (scsicmd[0] == READ_16 || scsicmd[0] == WRITE_16) {		/* rule out impossible LBAs and sector counts */		if (scsicmd[2] || scsicmd[3] || scsicmd[10] || scsicmd[11])			return 1;		if (lba48) {			tf->hob_nsect = scsicmd[12];			tf->hob_lbal = scsicmd[6];			tf->hob_lbam = scsicmd[5];			tf->hob_lbah = scsicmd[4];			qc->nsect = ((unsigned int)scsicmd[12] << 8) |					scsicmd[13];		} else {			/* once again, filter out impossible non-zero values */			if (scsicmd[4] || scsicmd[5] || scsicmd[12] ||			    (scsicmd[6] & 0xf0))				return 1;			/* stores LBA27:24 in lower 4 bits of device reg */			tf->device |= scsicmd[2];			qc->nsect = scsicmd[13];		}		tf->nsect = scsicmd[13];		tf->lbal = scsicmd[9];		tf->lbam = scsicmd[8];		tf->lbah = scsicmd[7];		VPRINTK("sixteen-byte command\n");		return 0;	}	DPRINTK("no-byte command\n");	return 1;}static int ata_scsi_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)))		ata_to_sense_error(qc, drv_stat);	else		cmd->result = SAM_STAT_GOOD;	qc->scsidone(cmd);	return 0;}/** *	ata_scsi_translate - Translate then issue SCSI command to ATA device *	@ap: ATA port to which the command is addressed *	@dev: ATA device to which the command is addressed *	@cmd: SCSI command to execute *	@done: SCSI command completion function *	@xlat_func: Actor which translates @cmd to an ATA taskfile * *	Our ->queuecommand() function has decided that the SCSI *	command issued can be directly translated into an ATA *	command, rather than handled internally. * *	This function sets up an ata_queued_cmd structure for the *	SCSI command, and sends that ata_queued_cmd to the hardware. * *	LOCKING: *	spin_lock_irqsave(host_set lock) */static void ata_scsi_translate(struct ata_port *ap, struct ata_device *dev,			      struct scsi_cmnd *cmd,			      void (*done)(struct scsi_cmnd *),			      ata_xlat_func_t xlat_func){	struct ata_queued_cmd *qc;	u8 *scsicmd = cmd->cmnd;	VPRINTK("ENTER\n");	qc = ata_scsi_qc_new(ap, dev, cmd, done);	if (!qc)		return;	/* data is present; dma-map it */	if (cmd->sc_data_direction == SCSI_DATA_READ ||	    cmd->sc_data_direction == SCSI_DATA_WRITE) {		if (unlikely(cmd->request_bufflen < 1)) {			printk(KERN_WARNING "ata%u(%u): WARNING: zero len r/w req\n",			       ap->id, dev->devno);			goto err_out;		}		if (cmd->use_sg)			ata_sg_init(qc, cmd->request_buffer, cmd->use_sg);		else			ata_sg_init_one(qc, cmd->request_buffer,					cmd->request_bufflen);		qc->pci_dma_dir = scsi_to_pci_dma_dir(cmd->sc_data_direction);	}	qc->complete_fn = ata_scsi_qc_complete;	if (xlat_func(qc, scsicmd))		goto err_out;	/* select device, send command to hardware */	if (ata_qc_issue(qc))		goto err_out;	VPRINTK("EXIT\n");	return;err_out:	ata_bad_cdb(cmd, done);	DPRINTK("EXIT - badcmd\n");}/** *	ata_scsi_rbuf_get - Map response buffer. *	@cmd: SCSI command containing buffer to be mapped. *	@buf_out: Pointer to mapped area. * *	Maps buffer contained within SCSI command @cmd. * *	LOCKING: *	spin_lock_irqsave(host_set lock) * *	RETURNS: *	Length of response buffer. */static unsigned int ata_scsi_rbuf_get(struct scsi_cmnd *cmd, u8 **buf_out){	u8 *buf;	unsigned int buflen;	if (cmd->use_sg) {		struct scatterlist *sg;		sg = (struct scatterlist *) cmd->request_buffer;		buf = kmap_atomic(sg->page, KM_USER0) + sg->offset;		buflen = sg->length;	} else {		buf = cmd->request_buffer;		buflen = cmd->request_bufflen;	}	memset(buf, 0, buflen);	*buf_out = buf;	return buflen;}/** *	ata_scsi_rbuf_put - Unmap response buffer. *	@cmd: SCSI command containing buffer to be unmapped. * *	Unmaps response buffer contained within @cmd. * *	LOCKING: *	spin_lock_irqsave(host_set lock) */static inline void ata_scsi_rbuf_put(struct scsi_cmnd *cmd){	if (cmd->use_sg) {		struct scatterlist *sg;		sg = (struct scatterlist *) cmd->request_buffer;		kunmap_atomic(sg->page, KM_USER0);	}}/** *	ata_scsi_rbuf_fill - wrapper for SCSI command simulators *	@args: Port / device / SCSI command of interest. *	@actor: Callback hook for desired SCSI command simulator * *	Takes care of the hard work of simulating a SCSI command... *	Mapping the response buffer, calling the command's handler, *	and handling the handler's return value.  This return value *	indicates whether the handler wishes the SCSI command to be *	completed successfully, or not. * *	LOCKING: *	spin_lock_irqsave(host_set lock) */void ata_scsi_rbuf_fill(struct ata_scsi_args *args,		        unsigned int (*actor) (struct ata_scsi_args *args,			     		   u8 *rbuf, unsigned int buflen)){	u8 *rbuf;	unsigned int buflen, rc;	struct scsi_cmnd *cmd = args->cmd;	buflen = ata_scsi_rbuf_get(cmd, &rbuf);	rc = actor(args, rbuf, buflen);	ata_scsi_rbuf_put(cmd);	if (rc)		ata_bad_cdb(cmd, args->done);	else {		cmd->result = SAM_STAT_GOOD;		args->done(cmd);	}}/** *	ata_scsiop_inq_std - Simulate INQUIRY command *	@args: Port / device / SCSI command of interest. *	@rbuf: Response buffer, to which simulated SCSI cmd output is sent. *	@buflen: Response buffer length. * *	Returns standard device identification data associated *	with non-EVPD INQUIRY command output. * *	LOCKING: *	spin_lock_irqsave(host_set lock) */unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,			       unsigned int buflen){	struct ata_device *dev = args->dev;	u8 hdr[] = {		TYPE_DISK,		0,		0x5,	/* claim SPC-3 version compatibility */		2,		95 - 4	};	/* set scsi removeable (RMB) bit per ata bit */	if (ata_id_removeable(dev))		hdr[1] |= (1 << 7);	VPRINTK("ENTER\n");	memcpy(rbuf, hdr, sizeof(hdr));	if (buflen > 35) {		memcpy(&rbuf[8], "ATA     ", 8);		ata_dev_id_string(dev, &rbuf[16], ATA_ID_PROD_OFS, 16);		ata_dev_id_string(dev, &rbuf[32], ATA_ID_FW_REV_OFS, 4);		if (rbuf[32] == 0 || rbuf[32] == ' ')			memcpy(&rbuf[32], "n/a ", 4);	}	if (buflen > 63) {		const u8 versions[] = {			0x60,	/* SAM-3 (no version claimed) */			0x03,			0x20,	/* SBC-2 (no version claimed) */			0x02,			0x60	/* SPC-3 (no version claimed) */		};		memcpy(rbuf + 59, versions, sizeof(versions));	}	return 0;}/** *	ata_scsiop_inq_00 - Simulate INQUIRY EVPD page 0, list of pages *	@args: Port / device / SCSI command of interest. *	@rbuf: Response buffer, to which simulated SCSI cmd output is sent. *	@buflen: Response buffer length. * *	Returns list of inquiry EVPD pages available. * *	LOCKING: *	spin_lock_irqsave(host_set lock) */unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf,			      unsigned int buflen){	const u8 pages[] = {		0x00,	/* page 0x00, this page */		0x80,	/* page 0x80, unit serial no page */		0x83	/* page 0x83, device ident page */	};	rbuf[3] = sizeof(pages);	/* number of supported EVPD pages */	if (buflen > 6)		memcpy(rbuf + 4, pages, sizeof(pages));	return 0;}/** *	ata_scsiop_inq_80 - Simulate INQUIRY EVPD page 80, device serial number *	@args: Port / device / SCSI command of interest. *	@rbuf: Response buffer, to which simulated SCSI cmd output is sent. *	@buflen: Response buffer length. * *	Returns ATA device serial number. * *	LOCKING: *	spin_lock_irqsave(host_set lock) */unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf,			      unsigned int buflen){	const u8 hdr[] = {		0,		0x80,			/* this page code */		0,		ATA_SERNO_LEN,		/* page len */	};	memcpy(rbuf, hdr, sizeof(hdr));	if (buflen > (ATA_SERNO_LEN + 4))		ata_dev_id_string(args->dev, (unsigned char *) &rbuf[4],				  ATA_ID_SERNO_OFS, ATA_SERNO_LEN);	return 0;}static const char *inq_83_str = "Linux ATA-SCSI simulator";/** *	ata_scsiop_inq_83 - Simulate INQUIRY EVPD page 83, device identity *	@args: Port / device / SCSI command of interest. *	@rbuf: Response buffer, to which simulated SCSI cmd output is sent. *	@buflen: Response buffer length. * *	Returns device identification.  Currently hardcoded to *	return "Linux ATA-SCSI simulator". * *	LOCKING: *	spin_lock_irqsave(host_set lock) */unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf,			      unsigned int buflen){	rbuf[1] = 0x83;			/* this page code */	rbuf[3] = 4 + strlen(inq_83_str);	/* page len */	/* our one and only identification descriptor (vendor-specific) */	if (buflen > (strlen(inq_83_str) + 4 + 4)) {		rbuf[4 + 0] = 2;	/* code set: ASCII */		rbuf[4 + 3] = strlen(inq_83_str);		memcpy(rbuf + 4 + 4, inq_83_str, strlen(inq_83_str));	}	return 0;}/** *	ata_scsiop_noop - *	@args: Port / device / SCSI command of interest. *	@rbuf: Response buffer, to which simulated SCSI cmd output is sent. *	@buflen: Response buffer length. * *	No operation.  Simply returns success to caller, to indicate *	that the caller should successfully complete this SCSI command. * *	LOCKING: *	spin_lock_irqsave(host_set lock) */unsigned int ata_scsiop_noop(struct ata_scsi_args *args, u8 *rbuf,			    unsigned int buflen){	VPRINTK("ENTER\n");	return 0;}/** *	ata_msense_push - Push data onto MODE SENSE data output buffer *	@ptr_io: (input/output) Location to store more output data *	@last: End of output data buffer *	@buf: Pointer to BLOB being added to output buffer *	@buflen: Length of BLOB * *	Store MODE SENSE data on an output buffer. * *	LOCKING: *	None. */static void ata_msense_push(u8 **ptr_io, const u8 *last,			    const u8 *buf, unsigned int buflen){	u8 *ptr = *ptr_io;	if ((ptr + buflen - 1) > last)		return;	memcpy(ptr, buf, buflen);	ptr += buflen;	*ptr_io = ptr;}/** *	ata_msense_caching - Simulate MODE SENSE caching info page *	@dev: Device associated with this MODE SENSE command *	@ptr_io: (input/output) Location to store more output data *	@last: End of output data buffer * *	Generate a caching info page, which conditionally indicates *	write caching to the SCSI layer, depending on device *	capabilities. * *	LOCKING: *	None. */static unsigned int ata_msense_caching(struct ata_device *dev, u8 **ptr_io,				       const u8 *last){	u8 page[] = {		0x8,				/* page code */		0x12,				/* page length */		0, 0, 0, 0, 0, 0, 0, 0, 0, 0,	/* 10 zeroes */		0, 0, 0, 0, 0, 0, 0, 0		/* 8 zeroes */	};	if (ata_id_wcache_enabled(dev))		page[2] |= (1 << 2);	/* write cache enable */	if (!ata_id_rahead_enabled(dev))		page[12] |= (1 << 5);	/* disable read ahead */	ata_msense_push(ptr_io, last, page, sizeof(page));	return sizeof(page);}/** *	ata_msense_ctl_mode - Simulate MODE SENSE control mode page *	@dev: Device associated with this MODE SENSE command *	@ptr_io: (input/output) Location to store more output data *	@last: End of output data buffer * *	Generate a generic MODE SENSE control mode page. * *	LOCKING: *	None. */static unsigned int ata_msense_ctl_mode(u8 **ptr_io, const u8 *last){	const u8 page[] = {0xa, 0xa, 2, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 30};	ata_msense_push(ptr_io, last, page, sizeof(page));	return sizeof(page);}/** *	ata_msense_rw_recovery - Simulate MODE SENSE r/w error recovery page *	@dev: Device associated with this MODE SENSE command *	@ptr_io: (input/output) Location to store more output data *	@last: End of output data buffer *

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?