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