📄 ide-scsi.c
字号:
/* * 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/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 <linux/scatterlist.h>#include <linux/delay.h>#include <linux/mutex.h>#include <linux/bitops.h>#include <asm/io.h>#include <asm/uaccess.h>#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include <scsi/scsi_tcq.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 */ unsigned int sg_cnt; /* Number of entries in sg */ int b_count; /* Bytes transferred from current entry */ struct scsi_cmnd *scsi_cmd; /* SCSI command */ void (*done)(struct 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_TIMEDOUT 3 /* command timed out */#define PC_DMA_OK 4 /* Use DMA *//* * SCSI command transformation layer */#define IDESCSI_SG_TRANSFORM 1 /* /dev/sg transformation *//* * Log flags */#define IDESCSI_LOG_CMD 0 /* Log SCSI commands */typedef struct ide_scsi_obj { ide_drive_t *drive; ide_driver_t *driver; struct gendisk *disk; struct Scsi_Host *host; 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 DEFINE_MUTEX(idescsi_ref_mutex);static int idescsi_nocd; /* Set by module param to skip cd */#define ide_scsi_g(disk) \ container_of((disk)->private_data, struct ide_scsi_obj, driver)static struct ide_scsi_obj *ide_scsi_get(struct gendisk *disk){ struct ide_scsi_obj *scsi = NULL; mutex_lock(&idescsi_ref_mutex); scsi = ide_scsi_g(disk); if (scsi) scsi_host_get(scsi->host); mutex_unlock(&idescsi_ref_mutex); return scsi;}static void ide_scsi_put(struct ide_scsi_obj *scsi){ mutex_lock(&idescsi_ref_mutex); scsi_host_put(scsi->host); mutex_unlock(&idescsi_ref_mutex);}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) { count = min(pc->sg->length - pc->b_count, bcount); if (PageHighMem(sg_page(pc->sg))) { unsigned long flags; local_irq_save(flags); buf = kmap_atomic(sg_page(pc->sg), KM_IRQ0) + pc->sg->offset; drive->hwif->atapi_input_bytes(drive, buf + pc->b_count, count); kunmap_atomic(buf - pc->sg->offset, KM_IRQ0); local_irq_restore(flags); } else { buf = sg_virt(pc->sg); drive->hwif->atapi_input_bytes(drive, buf + pc->b_count, count); } bcount -= count; pc->b_count += count; if (pc->b_count == pc->sg->length) { if (!--pc->sg_cnt) break; pc->sg = sg_next(pc->sg); pc->b_count = 0; } } if (bcount) { printk (KERN_ERR "ide-scsi: scatter gather table too small, discarding data\n"); idescsi_discard_data (drive, bcount); }}static void idescsi_output_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigned int bcount){ int count; char *buf; while (bcount) { count = min(pc->sg->length - pc->b_count, bcount); if (PageHighMem(sg_page(pc->sg))) { unsigned long flags; local_irq_save(flags); buf = kmap_atomic(sg_page(pc->sg), KM_IRQ0) + pc->sg->offset; drive->hwif->atapi_output_bytes(drive, buf + pc->b_count, count); kunmap_atomic(buf - pc->sg->offset, KM_IRQ0); local_irq_restore(flags); } else { buf = sg_virt(pc->sg); drive->hwif->atapi_output_bytes(drive, buf + pc->b_count, count); } bcount -= count; pc->b_count += count; if (pc->b_count == pc->sg->length) { if (!--pc->sg_cnt) break; pc->sg = sg_next(pc->sg); pc->b_count = 0; } } if (bcount) { printk (KERN_ERR "ide-scsi: scatter gather table too small, padding with zeros\n"); idescsi_output_zeros (drive, bcount); }}static void ide_scsi_hex_dump(u8 *data, int len){ print_hex_dump(KERN_CONT, "", DUMP_PREFIX_NONE, 16, 1, data, len, 0);}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 = kzalloc(sizeof(idescsi_pc_t), GFP_ATOMIC); rq = kmalloc(sizeof(struct request), GFP_ATOMIC); buf = kzalloc(SCSI_SENSE_BUFFERSIZE, GFP_ATOMIC); if (!pc || !rq || !buf) { kfree(buf); kfree(rq); kfree(pc); return -ENOMEM; } 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->cmd_type = REQ_TYPE_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); ide_scsi_hex_dump(pc->c, 6); } rq->rq_disk = scsi->disk; return ide_do_drive_cmd(drive, rq, ide_preempt);}static int idescsi_end_request(ide_drive_t *, int, int);static ide_startstop_tidescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err){ if (HWIF(drive)->INB(IDE_STATUS_REG) & (BUSY_STAT|DRQ_STAT)) /* force an abort */ HWIF(drive)->OUTB(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG); rq->errors++; idescsi_end_request(drive, 0, 0); return ide_stopped;}static ide_startstop_tidescsi_atapi_abort(ide_drive_t *drive, struct request *rq){#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; idescsi_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; int errors = rq->errors; unsigned long flags; if (!blk_special_request(rq) && !blk_sense_request(rq)) { ide_end_request(drive, uptodate, nrsecs); return 0; } ide_end_drive_cmd (drive, 0, 0); if (blk_sense_request(rq)) { 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); ide_scsi_hex_dump(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) | ((test_bit(PC_TIMEDOUT, &pc->flags)?DID_TIME_OUT:DID_OK) << 16); } else if (test_bit(PC_TIMEDOUT, &pc->flags)) { if (log) printk (KERN_WARNING "ide-scsi: %s: timed out for %lu\n", drive->name, pc->scsi_cmd->serial_number); pc->scsi_cmd->result = DID_TIME_OUT << 16; } else if (errors >= ERROR_MAX) { pc->scsi_cmd->result = DID_ERROR << 16; if (log) printk ("ide-scsi: %s: I/O error for %lu\n", drive->name, pc->scsi_cmd->serial_number); } else if (errors) { if (log) printk ("ide-scsi: %s: check condition for %lu\n", drive->name, pc->scsi_cmd->serial_number); if (!idescsi_check_condition(drive, rq)) /* we started a request sense, so we'll be back, exit for now */ return 0; pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -