eata_pio.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 997 行 · 第 1/2 页
C
997 行
/************************************************************ * * * Linux EATA SCSI PIO driver * * * * based on the CAM document CAM/89-004 rev. 2.0c, * * DPT's driver kit, some internal documents and source, * * and several other Linux scsi drivers and kernel docs. * * * * The driver currently: * * -supports all EATA-PIO boards * * -only supports DASD devices * * * * (c)1993-96 Michael Neuffer, Alfred Arnold * * neuffer@goofy.zdv.uni-mainz.de * * a.arnold@kfa-juelich.de * * * * Updated 2002 by Alan Cox <alan@redhat.com> for Linux * * 2.5.x and the newer locking and error handling * * * * This program is free software; you can redistribute it * * and/or modify it under the terms of the GNU General * * Public License as published by the Free Software * * Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be * * useful, but WITHOUT ANY WARRANTY; without even the * * implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License * * for more details. * * * * You should have received a copy of the GNU General * * Public License along with this kernel; if not, write to * * the Free Software Foundation, Inc., 675 Mass Ave, * * Cambridge, MA 02139, USA. * * * * For the avoidance of doubt the "preferred form" of this * * code is one which is in an open non patent encumbered * * format. Where cryptographic key signing forms part of * * the process of creating an executable the information * * including keys needed to generate an equivalently * * functional executable are deemed to be part of the * * source code are deemed to be part of the source code. * * * ************************************************************ * last change: 2002/11/02 OS: Linux 2.5.45 * ************************************************************/#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/in.h>#include <linux/pci.h>#include <linux/proc_fs.h>#include <linux/interrupt.h>#include <linux/blkdev.h>#include <linux/spinlock.h>#include <linux/delay.h>#include <asm/io.h>#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include "eata_generic.h"#include "eata_pio.h"static uint ISAbases[MAXISA] = { 0x1F0, 0x170, 0x330, 0x230};static uint ISAirqs[MAXISA] = { 14, 12, 15, 11};static unsigned char EISAbases[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };static uint registered_HBAs;static struct Scsi_Host *last_HBA;static struct Scsi_Host *first_HBA;static unsigned char reg_IRQ[16];static unsigned char reg_IRQL[16];static unsigned long int_counter;static unsigned long queue_counter;static struct scsi_host_template driver_template;/* * eata_proc_info * inout : decides on the direction of the dataflow and the meaning of the * variables * buffer: If inout==FALSE data is being written to it else read from it * *start: If inout==FALSE start of the valid data in the buffer * offset: If inout==FALSE offset from the beginning of the imaginary file * from which we start writing into the buffer * length: If inout==FALSE max number of bytes to be written into the buffer * else number of bytes in the buffer */static int eata_pio_proc_info(struct Scsi_Host *shost, char *buffer, char **start, off_t offset, int length, int rw){ static u8 buff[512]; int size, len = 0; off_t begin = 0, pos = 0; if (rw) return -ENOSYS; if (offset == 0) memset(buff, 0, sizeof(buff)); size = sprintf(buffer+len, "EATA (Extended Attachment) PIO driver version: " "%d.%d%s\n",VER_MAJOR, VER_MINOR, VER_SUB); len += size; pos = begin + len; size = sprintf(buffer + len, "queued commands: %10ld\n" "processed interrupts:%10ld\n", queue_counter, int_counter); len += size; pos = begin + len; size = sprintf(buffer + len, "\nscsi%-2d: HBA %.10s\n", shost->host_no, SD(shost)->name); len += size; pos = begin + len; size = sprintf(buffer + len, "Firmware revision: v%s\n", SD(shost)->revision); len += size; pos = begin + len; size = sprintf(buffer + len, "IO: PIO\n"); len += size; pos = begin + len; size = sprintf(buffer + len, "Base IO : %#.4x\n", (u32) shost->base); len += size; pos = begin + len; size = sprintf(buffer + len, "Host Bus: %s\n", (SD(shost)->bustype == 'P')?"PCI ": (SD(shost)->bustype == 'E')?"EISA":"ISA "); len += size; pos = begin + len; if (pos < offset) { len = 0; begin = pos; } if (pos > offset + length) goto stop_output; stop_output: DBG(DBG_PROC, printk("2pos: %ld offset: %ld len: %d\n", pos, offset, len)); *start=buffer+(offset-begin); /* Start of wanted data */ len-=(offset-begin); /* Start slop */ if(len>length) len = length; /* Ending slop */ DBG(DBG_PROC, printk("3pos: %ld offset: %ld len: %d\n", pos, offset, len)); return (len); }static int eata_pio_release(struct Scsi_Host *sh){ if (sh->irq && reg_IRQ[sh->irq] == 1) free_irq(sh->irq, NULL); else reg_IRQ[sh->irq]--; if (SD(sh)->channel == 0) { if (sh->io_port && sh->n_io_port) release_region(sh->io_port, sh->n_io_port); } return 1;}static void IncStat(struct scsi_pointer *SCp, uint Increment){ SCp->ptr += Increment; if ((SCp->this_residual -= Increment) == 0) { if ((--SCp->buffers_residual) == 0) SCp->Status = 0; else { SCp->buffer++; SCp->ptr = page_address(SCp->buffer->page) + SCp->buffer->offset; SCp->this_residual = SCp->buffer->length; } }}static void eata_pio_int_handler(int irq, void *dev_id, struct pt_regs *regs);static irqreturn_t do_eata_pio_int_handler(int irq, void *dev_id, struct pt_regs *regs){ unsigned long flags; struct Scsi_Host *dev = dev_id; spin_lock_irqsave(dev->host_lock, flags); eata_pio_int_handler(irq, dev_id, regs); spin_unlock_irqrestore(dev->host_lock, flags); return IRQ_HANDLED;}static void eata_pio_int_handler(int irq, void *dev_id, struct pt_regs *regs){ uint eata_stat = 0xfffff; struct scsi_cmnd *cmd; hostdata *hd; struct eata_ccb *cp; uint base; uint x, z; struct Scsi_Host *sh; unsigned short zwickel = 0; unsigned char stat, odd; for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->prev) { if (sh->irq != irq) continue; if (inb((uint) sh->base + HA_RSTATUS) & HA_SBUSY) continue; int_counter++; hd = SD(sh); cp = &hd->ccb[0]; cmd = cp->cmd; base = (uint) cmd->device->host->base; do { stat = inb(base + HA_RSTATUS); if (stat & HA_SDRQ) { if (cp->DataIn) { z = 256; odd = 0; while ((cmd->SCp.Status) && ((z > 0) || (odd))) { if (odd) { *(cmd->SCp.ptr) = zwickel >> 8; IncStat(&cmd->SCp, 1); odd = 0; } x = min_t(unsigned int, z, cmd->SCp.this_residual / 2); insw(base + HA_RDATA, cmd->SCp.ptr, x); z -= x; IncStat(&cmd->SCp, 2 * x); if ((z > 0) && (cmd->SCp.this_residual == 1)) { zwickel = inw(base + HA_RDATA); *(cmd->SCp.ptr) = zwickel & 0xff; IncStat(&cmd->SCp, 1); z--; odd = 1; } } while (z > 0) { zwickel = inw(base + HA_RDATA); z--; } } else { /* cp->DataOut */ odd = 0; z = 256; while ((cmd->SCp.Status) && ((z > 0) || (odd))) { if (odd) { zwickel += *(cmd->SCp.ptr) << 8; IncStat(&cmd->SCp, 1); outw(zwickel, base + HA_RDATA); z--; odd = 0; } x = min_t(unsigned int, z, cmd->SCp.this_residual / 2); outsw(base + HA_RDATA, cmd->SCp.ptr, x); z -= x; IncStat(&cmd->SCp, 2 * x); if ((z > 0) && (cmd->SCp.this_residual == 1)) { zwickel = *(cmd->SCp.ptr); zwickel &= 0xff; IncStat(&cmd->SCp, 1); odd = 1; } } while (z > 0 || odd) { outw(zwickel, base + HA_RDATA); z--; odd = 0; } } } } while ((stat & HA_SDRQ) || ((stat & HA_SMORE) && hd->moresupport)); /* terminate handler if HBA goes busy again, i.e. transfers * more data */ if (stat & HA_SBUSY) break; /* OK, this is quite stupid, but I haven't found any correct * way to get HBA&SCSI status so far */ if (!(inb(base + HA_RSTATUS) & HA_SERROR)) { cmd->result = (DID_OK << 16); hd->devflags |= (1 << cp->cp_id); } else if (hd->devflags & 1 << cp->cp_id) cmd->result = (DID_OK << 16) + 0x02; else cmd->result = (DID_NO_CONNECT << 16); if (cp->status == LOCKED) { cp->status = FREE; eata_stat = inb(base + HA_RSTATUS); printk(KERN_CRIT "eata_pio: int_handler, freeing locked " "queueslot\n"); return; }#if DBG_INTR2 if (stat != 0x50) printk(KERN_DEBUG "stat: %#.2x, result: %#.8x\n", stat, cmd->result);#endif cp->status = FREE; /* now we can release the slot */ cmd->scsi_done(cmd); } return;}static inline uint eata_pio_send_command(uint base, unsigned char command){ uint loop = HZ / 2; while (inb(base + HA_RSTATUS) & HA_SBUSY) if (--loop == 0) return 1; /* Enable interrupts for HBA. It is not the best way to do it at this * place, but I hope that it doesn't interfere with the IDE driver * initialization this way */ outb(HA_CTRL_8HEADS, base + HA_CTRLREG); outb(command, base + HA_WCOMMAND); return 0;}static int eata_pio_queue(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)){ uint x, y; uint base; hostdata *hd; struct Scsi_Host *sh; struct eata_ccb *cp; queue_counter++; hd = HD(cmd); sh = cmd->device->host; base = (uint) sh->base; /* use only slot 0, as 2001 can handle only one cmd at a time */ y = x = 0; if (hd->ccb[y].status != FREE) { DBG(DBG_QUEUE, printk(KERN_EMERG "can_queue %d, x %d, y %d\n", sh->can_queue, x, y));#if DEBUG_EATA panic(KERN_EMERG "eata_pio: run out of queue slots cmdno:%ld " "intrno: %ld\n", queue_counter, int_counter);#else panic(KERN_EMERG "eata_pio: run out of queue slots....\n");#endif } cp = &hd->ccb[y]; memset(cp, 0, sizeof(struct eata_ccb)); memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); cp->status = USED; /* claim free slot */ DBG(DBG_QUEUE, printk(KERN_DEBUG "eata_pio_queue pid %ld, target: %x, lun:" " %x, y %d\n", cmd->pid, cmd->device->id, cmd->device->lun, y)); cmd->scsi_done = (void *) done; if (cmd->sc_data_direction == DMA_TO_DEVICE) cp->DataOut = 1; /* Output mode */ else cp->DataIn = 0; /* Input mode */ cp->Interpret = (cmd->device->id == hd->hostid); cp->cp_datalen = htonl((unsigned long) cmd->request_bufflen); cp->Auto_Req_Sen = 0; cp->cp_reqDMA = htonl(0); cp->reqlen = 0; cp->cp_id = cmd->device->id; cp->cp_lun = cmd->device->lun; cp->cp_dispri = 0; cp->cp_identify = 1; memcpy(cp->cp_cdb, cmd->cmnd, COMMAND_SIZE(*cmd->cmnd)); cp->cp_statDMA = htonl(0); cp->cp_viraddr = cp; cp->cmd = cmd; cmd->host_scribble = (char *) &hd->ccb[y]; if (cmd->use_sg == 0) { cmd->SCp.buffers_residual = 1; cmd->SCp.ptr = cmd->request_buffer; cmd->SCp.this_residual = cmd->request_bufflen; cmd->SCp.buffer = NULL; } else { cmd->SCp.buffer = cmd->request_buffer; cmd->SCp.buffers_residual = cmd->use_sg; cmd->SCp.ptr = page_address(cmd->SCp.buffer->page) + cmd->SCp.buffer->offset; cmd->SCp.this_residual = cmd->SCp.buffer->length; } cmd->SCp.Status = (cmd->SCp.this_residual != 0); /* TRUE as long as bytes * are to transfer */ if (eata_pio_send_command(base, EATA_CMD_PIO_SEND_CP)) { cmd->result = DID_BUS_BUSY << 16; printk(KERN_NOTICE "eata_pio_queue target %d, pid %ld, HBA busy, " "returning DID_BUS_BUSY, done.\n", cmd->device->id, cmd->pid); done(cmd); cp->status = FREE; return (0); } /* FIXME: timeout */ while (!(inb(base + HA_RSTATUS) & HA_SDRQ)) cpu_relax(); outsw(base + HA_RDATA, cp, hd->cplen); outb(EATA_CMD_PIO_TRUNC, base + HA_WCOMMAND); for (x = 0; x < hd->cppadlen; x++) outw(0, base + HA_RDATA); DBG(DBG_QUEUE, printk(KERN_DEBUG "Queued base %#.4lx pid: %ld target: %x " "lun: %x slot %d irq %d\n", (long) sh->base, cmd->pid, cmd->device->id, cmd->device->lun, y, sh->irq)); return (0);}static int eata_pio_abort(struct scsi_cmnd *cmd){ uint loop = HZ; DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_abort called pid: %ld " "target: %x lun: %x reason %x\n", cmd->pid, cmd->device->id, cmd->device->lun, cmd->abort_reason)); while (inb(cmd->device->host->base + HA_RAUXSTAT) & HA_ABUSY) if (--loop == 0) { printk(KERN_WARNING "eata_pio: abort, timeout error.\n"); return FAILED; } if (CD(cmd)->status == FREE) { DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_NOT_RUNNING\n")); return FAILED; } if (CD(cmd)->status == USED) { DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_BUSY\n")); /* We want to sleep a bit more here */ return FAILED; /* SNOOZE */ } if (CD(cmd)->status == RESET) { printk(KERN_WARNING "eata_pio: abort, command reset error.\n"); return FAILED; } if (CD(cmd)->status == LOCKED) { DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio: abort, queue slot " "locked.\n")); return FAILED; } panic("eata_pio: abort: invalid slot status\n");}static int eata_pio_host_reset(struct scsi_cmnd *cmd){ uint x, limit = 0; unsigned char success = 0; struct scsi_cmnd *sp; struct Scsi_Host *host = cmd->device->host; DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset called pid:%ld target:" " %x lun: %x reason %x\n", cmd->pid, cmd->device->id, cmd->device->lun, cmd->abort_reason)); if (HD(cmd)->state == RESET) { printk(KERN_WARNING "eata_pio_reset: exit, already in reset.\n"); return FAILED; } /* force all slots to be free */ for (x = 0; x < cmd->device->host->can_queue; x++) { if (HD(cmd)->ccb[x].status == FREE) continue;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?