📄 eata_pio.c
字号:
/************************************************************ * * * 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 * * * * 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. * * * ************************************************************ * last change: 96/07/16 OS: Linux 2.0.8 * ************************************************************//* Look in eata_pio.h for configuration information */#include <linux/module.h> #include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/malloc.h>#include <linux/in.h>#include <linux/pci.h>#include <linux/proc_fs.h>#include <asm/io.h>#include "eata_pio.h"#include "eata_dma_proc.h"#include "scsi.h"#include "sd.h"#include <linux/stat.h>#include <linux/config.h> /* for CONFIG_PCI */#include <linux/blk.h>#include <linux/spinlock.h>static uint ISAbases[MAXISA] ={0x1F0, 0x170, 0x330, 0x230};static uint ISAirqs[MAXISA] ={14,12,15,11};static unchar EISAbases[] ={1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};static uint registered_HBAs = 0;static struct Scsi_Host *last_HBA = NULL;static struct Scsi_Host *first_HBA = NULL;static unchar reg_IRQ[] ={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};static unchar reg_IRQL[] ={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};static ulong int_counter = 0;static ulong queue_counter = 0;#include "eata_pio_proc.c" #ifdef MODULEint 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(TRUE);}#endifvoid IncStat(Scsi_Pointer *SCp, uint Increment){ SCp->ptr+=Increment; if ((SCp->this_residual-=Increment)==0) { if ((--SCp->buffers_residual)==0) SCp->Status=FALSE; else { SCp->buffer++; SCp->ptr=SCp->buffer->address; SCp->this_residual=SCp->buffer->length; } }}void eata_pio_int_handler(int irq, void *dev_id, struct pt_regs * regs);void do_eata_pio_int_handler(int irq, void *dev_id, struct pt_regs * regs){ unsigned long flags; spin_lock_irqsave(&io_request_lock, flags); eata_pio_int_handler(irq, dev_id, regs); spin_unlock_irqrestore(&io_request_lock, flags);}void eata_pio_int_handler(int irq, void *dev_id, struct pt_regs * regs){ uint eata_stat = 0xfffff; Scsi_Cmnd *cmd; hostdata *hd; struct eata_ccb *cp; uint base; ulong flags; uint x,z; struct Scsi_Host *sh; ushort zwickel=0; unchar stat,odd; save_flags(flags); cli(); 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->host->base; do { stat=inb(base+HA_RSTATUS); if (stat&HA_SDRQ) { if (cp->DataIn) { z=256; odd=FALSE; while ((cmd->SCp.Status)&&((z>0)||(odd))) { if (odd) { *(cmd->SCp.ptr)=zwickel>>8; IncStat(&cmd->SCp,1); odd=FALSE; } x=min(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=TRUE; } } while (z>0) { zwickel=inw(base+HA_RDATA); z--; } } else /* cp->DataOut */ { odd=FALSE; 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=FALSE; } x=min(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=TRUE; } } while (z>0||odd) { outw(zwickel,base+HA_RDATA); z--; odd=FALSE; } } } } 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_NOTICE "eata_pio: int_handler, freeing locked " "queueslot\n"); DBG(DBG_INTR&&DBG_DELAY,DELAY(1)); restore_flags(flags); return; } #if DBG_INTR2 if (stat != 0x50) printk(KERN_DEBUG "stat: %#.2x, result: %#.8x\n", stat, cmd->result); DBG(DBG_INTR&&DBG_DELAY,DELAY(1));#endif cp->status = FREE; /* now we can release the slot */ restore_flags(flags); cmd->scsi_done(cmd); save_flags(flags); cli(); } restore_flags(flags); return;}inline uint eata_pio_send_command(uint base, unchar command){ uint loop = HZ/2; while (inb(base + HA_RSTATUS) & HA_SBUSY) if (--loop == 0) return(TRUE); /* 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(FALSE);}int eata_pio_queue(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)){ uint x, y; long flags; uint base; hostdata *hd; struct Scsi_Host *sh; struct eata_ccb *cp; save_flags(flags); cli(); queue_counter++; hd = HD(cmd); sh = cmd->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->target, cmd->lun, y)); DBG(DBG_QUEUE && DBG_DELAY, DELAY(1)); cmd->scsi_done = (void *)done; switch (cmd->cmnd[0]) { case CHANGE_DEFINITION: case COMPARE: case COPY: case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT: case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER: case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE: case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: case WRITE_6: case WRITE_10: case WRITE_VERIFY: case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME: case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12: case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG: case 0xea: /* alternate number for WRITE LONG */ cp->DataOut = TRUE; /* Output mode */ break; case TEST_UNIT_READY: default: cp->DataIn = TRUE; /* Input mode */ } cp->Interpret = (cmd->target == hd->hostid); cp->cp_datalen = htonl((ulong)cmd->request_bufflen); cp->Auto_Req_Sen = FALSE; cp->cp_reqDMA = htonl(0); cp->reqlen = 0; cp->cp_id = cmd->target; cp->cp_lun = cmd->lun; cp->cp_dispri = FALSE; cp->cp_identify = TRUE; 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 = cmd->SCp.buffer->address; 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->target, cmd->pid); done(cmd); cp->status = FREE; restore_flags(flags); return (0); } while (!(inb(base + HA_RSTATUS) & HA_SDRQ)); 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->target, cmd->lun, y, sh->irq)); DBG(DBG_QUEUE && DBG_DELAY, DELAY(1)); restore_flags(flags); return (0);}int eata_pio_abort(Scsi_Cmnd * cmd){ ulong flags; uint loop = HZ; save_flags(flags); cli(); DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_abort called pid: %ld " "target: %x lun: %x reason %x\n", cmd->pid, cmd->target, cmd->lun, cmd->abort_reason)); DBG(DBG_ABNORM && DBG_DELAY, DELAY(1)); while (inb((uint)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY) if (--loop == 0) { printk(KERN_WARNING "eata_pio: abort, timeout error.\n"); restore_flags(flags); DBG(DBG_ABNORM && DBG_DELAY, DELAY(1)); return (SCSI_ABORT_ERROR); } if (CD(cmd)->status == FREE) { DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_NOT_RUNNING\n")); restore_flags(flags); return (SCSI_ABORT_NOT_RUNNING); } if (CD(cmd)->status == USED) { DBG(DBG_ABNORM, printk(KERN_WARNING "Returning: SCSI_ABORT_BUSY\n")); restore_flags(flags); return (SCSI_ABORT_BUSY); /* SNOOZE */ } if (CD(cmd)->status == RESET) { restore_flags(flags); printk(KERN_WARNING "eata_pio: abort, command reset error.\n"); DBG(DBG_ABNORM && DBG_DELAY, DELAY(1)); return (SCSI_ABORT_ERROR); } if (CD(cmd)->status == LOCKED) { restore_flags(flags); DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio: abort, queue slot " "locked.\n")); DBG(DBG_ABNORM && DBG_DELAY, DELAY(1)); return (SCSI_ABORT_NOT_RUNNING); } restore_flags(flags); panic("eata_pio: abort: invalid slot status\n");}int eata_pio_reset(Scsi_Cmnd * cmd, unsigned int dummy){ uint x, time, limit = 0; ulong flags; unchar success = FALSE; Scsi_Cmnd *sp; save_flags(flags); cli(); DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset called pid:%ld target:" " %x lun: %x reason %x\n", cmd->pid, cmd->target, cmd->lun, cmd->abort_reason)); if (HD(cmd)->state == RESET) { printk(KERN_WARNING "eata_pio_reset: exit, already in reset.\n"); restore_flags(flags); DBG(DBG_ABNORM && DBG_DELAY, DELAY(1)); return (SCSI_RESET_ERROR); } /* force all slots to be free */ for (x = 0; x < cmd->host->can_queue; x++) { if (HD(cmd)->ccb[x].status == FREE) continue; sp = HD(cmd)->ccb[x].cmd; HD(cmd)->ccb[x].status = RESET; printk(KERN_WARNING "eata_pio_reset: slot %d in reset, pid %ld.\n", x, sp->pid); DBG(DBG_ABNORM && DBG_DELAY, DELAY(1)); if (sp == NULL) panic("eata_pio_reset: slot %d, sp==NULL.\n", x); DBG(DBG_ABNORM && DBG_DELAY, DELAY(1)); } /* hard reset the HBA */ outb(EATA_CMD_RESET, (uint) cmd->host->base+HA_WCOMMAND); DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: board reset done.\n")); HD(cmd)->state = RESET; time = jiffies; while (time_before(jiffies, time + 3 * HZ) && limit++ < 10000000); DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: interrupts disabled, " "loops %d.\n", limit)); DBG(DBG_ABNORM && DBG_DELAY, DELAY(1)); for (x = 0; x < cmd->host->can_queue; x++) { /* Skip slots already set free by interrupt */ if (HD(cmd)->ccb[x].status != RESET) continue; sp = HD(cmd)->ccb[x].cmd;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -