ide-scsi.c

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

C
1,160
字号
/* * linux/drivers/scsi/ide-scsi.c	Version 0.9		Jul   4, 1999 * * Copyright (C) 1996 - 1999 Gadi Oxman <gadio@netvision.net.il> *//* * Emulation of a SCSI host adapter for IDE ATAPI devices. * * With this driver, one can use the Linux SCSI drivers instead of the * native IDE ATAPI drivers. * * Ver 0.1   Dec  3 96   Initial version. * Ver 0.2   Jan 26 97   Fixed bug in cleanup_module() and added emulation *                        of MODE_SENSE_6/MODE_SELECT_6 for cdroms. Thanks *                        to Janos Farkas for pointing this out. *                       Avoid using bitfields in structures for m68k. *                       Added Scatter/Gather and DMA support. * Ver 0.4   Dec  7 97   Add support for ATAPI PD/CD drives. *                       Use variable timeout for each command. * Ver 0.5   Jan  2 98   Fix previous PD/CD support. *                       Allow disabling of SCSI-6 to SCSI-10 transformation. * Ver 0.6   Jan 27 98   Allow disabling of SCSI command translation layer *                        for access through /dev/sg. *                       Fix MODE_SENSE_6/MODE_SELECT_6/INQUIRY translation. * Ver 0.7   Dec 04 98   Ignore commands where lun != 0 to avoid multiple *                        detection of devices with CONFIG_SCSI_MULTI_LUN * Ver 0.8   Feb 05 99   Optical media need translation too. Reverse 0.7. * Ver 0.9   Jul 04 99   Fix a bug in SG_SET_TRANSFORM. * Ver 0.91  Jun 10 02   Fix "off by one" error in transforms * Ver 0.92  Dec 31 02   Implement new SCSI mid level API */#define IDESCSI_VERSION "0.92"#include <linux/module.h>#include <linux/config.h>#include <linux/types.h>#include <linux/string.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/ioport.h>#include <linux/blkdev.h>#include <linux/errno.h>#include <linux/hdreg.h>#include <linux/slab.h>#include <linux/ide.h>#include <asm/io.h>#include <asm/bitops.h>#include <asm/uaccess.h>#include "scsi.h"#include <scsi/scsi_host.h>#include <scsi/sg.h>#define IDESCSI_DEBUG_LOG		0typedef struct idescsi_pc_s {	u8 c[12];				/* Actual packet bytes */	int request_transfer;			/* Bytes to transfer */	int actually_transferred;		/* Bytes actually transferred */	int buffer_size;			/* Size of our data buffer */	struct request *rq;			/* The corresponding request */	u8 *buffer;				/* Data buffer */	u8 *current_position;			/* Pointer into the above buffer */	struct scatterlist *sg;			/* Scatter gather table */	int b_count;				/* Bytes transferred from current entry */	Scsi_Cmnd *scsi_cmd;			/* SCSI command */	void (*done)(Scsi_Cmnd *);		/* Scsi completion routine */	unsigned long flags;			/* Status/Action flags */	unsigned long timeout;			/* Command timeout */} idescsi_pc_t;/* *	Packet command status bits. */#define PC_DMA_IN_PROGRESS		0	/* 1 while DMA in progress */#define PC_WRITING			1	/* Data direction */#define PC_TRANSFORM			2	/* transform SCSI commands */#define PC_TIMEDOUT			3	/* command timed out */#define PC_DMA_OK			4	/* Use DMA *//* *	SCSI command transformation layer */#define IDESCSI_TRANSFORM		0	/* Enable/Disable transformation */#define IDESCSI_SG_TRANSFORM		1	/* /dev/sg transformation *//* *	Log flags */#define IDESCSI_LOG_CMD			0	/* Log SCSI commands */typedef struct {	ide_drive_t *drive;	idescsi_pc_t *pc;			/* Current packet command */	unsigned long flags;			/* Status/Action flags */	unsigned long transform;		/* SCSI cmd translation layer */	unsigned long log;			/* log flags */} idescsi_scsi_t;static inline idescsi_scsi_t *scsihost_to_idescsi(struct Scsi_Host *host){	return (idescsi_scsi_t*) (&host[1]);}static inline idescsi_scsi_t *drive_to_idescsi(ide_drive_t *ide_drive){	return scsihost_to_idescsi(ide_drive->driver_data);}/* *	Per ATAPI device status bits. */#define IDESCSI_DRQ_INTERRUPT		0	/* DRQ interrupt device *//* *	ide-scsi requests. */#define IDESCSI_PC_RQ			90static void idescsi_discard_data (ide_drive_t *drive, unsigned int bcount){	while (bcount--)		(void) HWIF(drive)->INB(IDE_DATA_REG);}static void idescsi_output_zeros (ide_drive_t *drive, unsigned int bcount){	while (bcount--)		HWIF(drive)->OUTB(0, IDE_DATA_REG);}/* *	PIO data transfer routines using the scatter gather table. */static void idescsi_input_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigned int bcount){	int count;	char *buf;	while (bcount) {		if (pc->sg - (struct scatterlist *) pc->scsi_cmd->request_buffer > pc->scsi_cmd->use_sg) {			printk (KERN_ERR "ide-scsi: scatter gather table too small, discarding data\n");			idescsi_discard_data (drive, bcount);			return;		}		count = min(pc->sg->length - pc->b_count, bcount);		buf = page_address(pc->sg->page) + pc->sg->offset;		atapi_input_bytes (drive, buf + pc->b_count, count);		bcount -= count; pc->b_count += count;		if (pc->b_count == pc->sg->length) {			pc->sg++;			pc->b_count = 0;		}	}}static void idescsi_output_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigned int bcount){	int count;	char *buf;	while (bcount) {		if (pc->sg - (struct scatterlist *) pc->scsi_cmd->request_buffer > pc->scsi_cmd->use_sg) {			printk (KERN_ERR "ide-scsi: scatter gather table too small, padding with zeros\n");			idescsi_output_zeros (drive, bcount);			return;		}		count = min(pc->sg->length - pc->b_count, bcount);		buf = page_address(pc->sg->page) + pc->sg->offset;		atapi_output_bytes (drive, buf + pc->b_count, count);		bcount -= count; pc->b_count += count;		if (pc->b_count == pc->sg->length) {			pc->sg++;			pc->b_count = 0;		}	}}/* *	Most of the SCSI commands are supported directly by ATAPI devices. *	idescsi_transform_pc handles the few exceptions. */static inline void idescsi_transform_pc1 (ide_drive_t *drive, idescsi_pc_t *pc){	u8 *c = pc->c, *scsi_buf = pc->buffer, *sc = pc->scsi_cmd->cmnd;	char *atapi_buf;	if (!test_bit(PC_TRANSFORM, &pc->flags))		return;	if (drive->media == ide_cdrom || drive->media == ide_optical) {		if (c[0] == READ_6 || c[0] == WRITE_6) {			c[8] = c[4];		c[5] = c[3];		c[4] = c[2];			c[3] = c[1] & 0x1f;	c[2] = 0;		c[1] &= 0xe0;			c[0] += (READ_10 - READ_6);		}		if (c[0] == MODE_SENSE || c[0] == MODE_SELECT) {			unsigned short new_len;			if (!scsi_buf)				return;			if ((atapi_buf = kmalloc(pc->buffer_size + 4, GFP_ATOMIC)) == NULL)				return;			memset(atapi_buf, 0, pc->buffer_size + 4);			memset (c, 0, 12);			c[0] = sc[0] | 0x40;			c[1] = sc[1];			c[2] = sc[2];			new_len = sc[4] + 4;			c[8] = new_len;			c[7] = new_len >> 8;			c[9] = sc[5];			if (c[0] == MODE_SELECT_10) {				atapi_buf[1] = scsi_buf[0];	/* Mode data length */				atapi_buf[2] = scsi_buf[1];	/* Medium type */				atapi_buf[3] = scsi_buf[2];	/* Device specific parameter */				atapi_buf[7] = scsi_buf[3];	/* Block descriptor length */				memcpy(atapi_buf + 8, scsi_buf + 4, pc->buffer_size - 4);			}			pc->buffer = atapi_buf;			pc->request_transfer += 4;			pc->buffer_size += 4;		}	}}static inline void idescsi_transform_pc2 (ide_drive_t *drive, idescsi_pc_t *pc){	u8 *atapi_buf = pc->buffer;	u8 *sc = pc->scsi_cmd->cmnd;	u8 *scsi_buf = pc->scsi_cmd->request_buffer;	if (!test_bit(PC_TRANSFORM, &pc->flags))		return;	if (drive->media == ide_cdrom || drive->media == ide_optical) {		if (pc->c[0] == MODE_SENSE_10 && sc[0] == MODE_SENSE) {			scsi_buf[0] = atapi_buf[1];		/* Mode data length */			scsi_buf[1] = atapi_buf[2];		/* Medium type */			scsi_buf[2] = atapi_buf[3];		/* Device specific parameter */			scsi_buf[3] = atapi_buf[7];		/* Block descriptor length */			memcpy(scsi_buf + 4, atapi_buf + 8, pc->request_transfer - 8);		}		if (pc->c[0] == INQUIRY) {			scsi_buf[2] |= 2;			/* ansi_revision */			scsi_buf[3] = (scsi_buf[3] & 0xf0) | 2;	/* response data format */		}	}	if (atapi_buf && atapi_buf != scsi_buf)		kfree(atapi_buf);}static inline void idescsi_free_bio (struct bio *bio){	struct bio *bhp;	while (bio) {		bhp = bio;		bio = bio->bi_next;		bio_put(bhp);	}}static void hexdump(u8 *x, int len){	int i;	printk("[ ");	for (i = 0; i < len; i++)		printk("%x ", x[i]);	printk("]\n");}static int idescsi_check_condition(ide_drive_t *drive, struct request *failed_command){	idescsi_scsi_t *scsi = drive_to_idescsi(drive);	idescsi_pc_t   *pc;	struct request *rq;	u8             *buf;	/* stuff a sense request in front of our current request */	pc = kmalloc (sizeof (idescsi_pc_t), GFP_ATOMIC);	rq = kmalloc (sizeof (struct request), GFP_ATOMIC);	buf = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_ATOMIC);	if (pc == NULL || rq == NULL || buf == NULL) {		if (pc) kfree(pc);		if (rq) kfree(rq);		if (buf) kfree(buf);		return -ENOMEM;	}	memset (pc, 0, sizeof (idescsi_pc_t));	memset (buf, 0, SCSI_SENSE_BUFFERSIZE);	ide_init_drive_cmd(rq);	rq->special = (char *) pc;	pc->rq = rq;	pc->buffer = buf;	pc->c[0] = REQUEST_SENSE;	pc->c[4] = pc->request_transfer = pc->buffer_size = SCSI_SENSE_BUFFERSIZE;	rq->flags = REQ_SENSE;	pc->timeout = jiffies + WAIT_READY;	/* NOTE! Save the failed packet command in "rq->buffer" */	rq->buffer = (void *) failed_command->special;	pc->scsi_cmd = ((idescsi_pc_t *) failed_command->special)->scsi_cmd;	if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) {		printk ("ide-scsi: %s: queue cmd = ", drive->name);		hexdump(pc->c, 6);	}	return ide_do_drive_cmd(drive, rq, ide_preempt);}ide_startstop_t idescsi_atapi_error (ide_drive_t *drive, const char *msg, byte stat){	struct request *rq;	byte err;	err = ide_dump_atapi_status(drive, msg, stat);	if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL)		return ide_stopped;	/* retry only "normal" I/O: */	if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK | REQ_DRIVE_TASKFILE)) {		rq->errors = 1;		ide_end_drive_cmd(drive, stat, err);		return ide_stopped;	}	if (HWIF(drive)->INB(IDE_STATUS_REG) & (BUSY_STAT|DRQ_STAT))		/* force an abort */		HWIF(drive)->OUTB(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG);	rq->errors++;	DRIVER(drive)->end_request(drive, 0, 0);	return ide_stopped;}ide_startstop_t idescsi_atapi_abort (ide_drive_t *drive, const char *msg){	struct request *rq;	if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL)	       return ide_stopped;	/* retry only "normal" I/O: */	if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK | REQ_DRIVE_TASKFILE)) {		rq->errors = 1;		ide_end_drive_cmd(drive, BUSY_STAT, 0);		return ide_stopped;	}#if IDESCSI_DEBUG_LOG	printk(KERN_WARNING "idescsi_atapi_abort called for %lu\n",			((idescsi_pc_t *) rq->special)->scsi_cmd->serial_number);#endif	rq->errors |= ERROR_MAX;	DRIVER(drive)->end_request(drive, 0, 0);	return ide_stopped;}static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs){	idescsi_scsi_t *scsi = drive_to_idescsi(drive);	struct request *rq = HWGROUP(drive)->rq;	idescsi_pc_t *pc = (idescsi_pc_t *) rq->special;	int log = test_bit(IDESCSI_LOG_CMD, &scsi->log);	struct Scsi_Host *host;	u8 *scsi_buf;	unsigned long flags;	if (!(rq->flags & (REQ_SPECIAL|REQ_SENSE))) {		ide_end_request(drive, uptodate, nrsecs);		return 0;	}	ide_end_drive_cmd (drive, 0, 0);	if (rq->flags & REQ_SENSE) {		idescsi_pc_t *opc = (idescsi_pc_t *) rq->buffer;		if (log) {			printk ("ide-scsi: %s: wrap up check %lu, rst = ", drive->name, opc->scsi_cmd->serial_number);			hexdump(pc->buffer,16);		}		memcpy((void *) opc->scsi_cmd->sense_buffer, pc->buffer, SCSI_SENSE_BUFFERSIZE);		kfree(pc->buffer);		kfree(pc);		kfree(rq);		pc = opc;		rq = pc->rq;		pc->scsi_cmd->result = (CHECK_CONDITION << 1) |

⌨️ 快捷键说明

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