📄 eata_dma.c
字号:
/************************************************************ * * * Linux EATA SCSI 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 ISA based EATA-DMA boards * * like PM2011, PM2021, PM2041, PM3021 * * -supports all EISA based EATA-DMA boards * * like PM2012B, PM2022, PM2122, PM2322, PM2042, * * PM3122, PM3222, PM3332 * * -supports all PCI based EATA-DMA boards * * like PM2024, PM2124, PM2044, PM2144, PM3224, * * PM3334 * * -supports the Wide, Ultra Wide and Differential * * versions of the boards * * -supports multiple HBAs with & without IRQ sharing * * -supports all SCSI channels on multi channel boards * * -supports ix86 and MIPS, untested on ALPHA * * -needs identical IDs on all channels of a HBA * * -can be loaded as module * * -displays statistical and hardware information * * in /proc/scsi/eata_dma * * -provides rudimentary latency measurement * * possibilities via /proc/scsi/eata_dma/<hostnum> * * * * (c)1993-96 Michael Neuffer * * mike@i-Connect.Net * * neuffer@mail.uni-mainz.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. * * * * I have to thank DPT for their excellent support. I took * * me almost a year and a stopover at their HQ, on my first * * trip to the USA, to get it, but since then they've been * * very helpful and tried to give me all the infos and * * support I need. * * * * Thanks also to Simon Shapiro, Greg Hosler and Mike * * Jagdis who did a lot of testing and found quite a number * * of bugs during the development. * ************************************************************ * last change: 96/10/21 OS: Linux 2.0.23 * ************************************************************//* Look in eata_dma.h for configuration and revision 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/bios32.h>#include <linux/pci.h>#include <linux/proc_fs.h>#include <linux/delay.h>#include <asm/byteorder.h>#include <asm/types.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/pgtable.h>#ifdef __mips__#include <asm/cachectl.h>#endif#include <linux/blk.h>#include "scsi.h"#include "sd.h"#include "hosts.h"#include "eata_dma.h"#include "eata_dma_proc.h" #include <linux/stat.h>#include <linux/config.h> /* for CONFIG_PCI */struct proc_dir_entry proc_scsi_eata_dma = { PROC_SCSI_EATA, 8, "eata_dma", S_IFDIR | S_IRUGO | S_IXUGO, 2};static u32 ISAbases[] ={0x1F0, 0x170, 0x330, 0x230};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 struct eata_sp *status = 0; /* Statuspacket array */static void *dma_scratch = 0;static struct eata_register *fake_int_base;static int fake_int_result;static int fake_int_happened;static ulong int_counter = 0;static ulong queue_counter = 0;void eata_scsi_done (Scsi_Cmnd * scmd){ scmd->request.rq_status = RQ_SCSI_DONE; if (scmd->request.sem != NULL) up(scmd->request.sem); return;} void eata_fake_int_handler(s32 irq, void *dev_id, struct pt_regs * regs){ fake_int_result = inb((ulong)fake_int_base + HA_RSTATUS); fake_int_happened = TRUE; DBG(DBG_INTR3, printk("eata_fake_int_handler called irq%d base %p" " res %#x\n", irq, fake_int_base, fake_int_result)); return;}#include "eata_dma_proc.c"#ifdef MODULEint eata_release(struct Scsi_Host *sh){ uint i; if (sh->irq && reg_IRQ[sh->irq] == 1) free_irq(sh->irq, NULL); else reg_IRQ[sh->irq]--; scsi_init_free((void *)status, 512); scsi_init_free((void *)dma_scratch - 4, 1024); for (i = 0; i < sh->can_queue; i++){ /* Free all SG arrays */ if(SD(sh)->ccb[i].sg_list != NULL) scsi_init_free((void *) SD(sh)->ccb[i].sg_list, sh->sg_tablesize * sizeof(struct eata_sg_list)); } if (SD(sh)->channel == 0) { if (sh->dma_channel != BUSMASTER) free_dma(sh->dma_channel); if (sh->io_port && sh->n_io_port) release_region(sh->io_port, sh->n_io_port); } return(TRUE);}#endifinline void eata_latency_in(struct eata_ccb *cp, hostdata *hd){ uint time; time = jiffies - cp->timestamp; if(hd->all_lat[1] > time) hd->all_lat[1] = time; if(hd->all_lat[2] < time) hd->all_lat[2] = time; hd->all_lat[3] += time; hd->all_lat[0]++; if((cp->rw_latency) == WRITE) { /* was WRITE */ if(hd->writes_lat[cp->sizeindex][1] > time) hd->writes_lat[cp->sizeindex][1] = time; if(hd->writes_lat[cp->sizeindex][2] < time) hd->writes_lat[cp->sizeindex][2] = time; hd->writes_lat[cp->sizeindex][3] += time; hd->writes_lat[cp->sizeindex][0]++; } else if((cp->rw_latency) == READ) { if(hd->reads_lat[cp->sizeindex][1] > time) hd->reads_lat[cp->sizeindex][1] = time; if(hd->reads_lat[cp->sizeindex][2] < time) hd->reads_lat[cp->sizeindex][2] = time; hd->reads_lat[cp->sizeindex][3] += time; hd->reads_lat[cp->sizeindex][0]++; }} inline void eata_latency_out(struct eata_ccb *cp, Scsi_Cmnd *cmd){ int x, z; short *sho; long *lon; x = 0; /* just to keep GCC quiet */ cp->timestamp = jiffies; /* For latency measurements */ switch(cmd->cmnd[0]) { case WRITE_6: x = cmd->cmnd[4]/2; cp->rw_latency = WRITE; break; case READ_6: x = cmd->cmnd[4]/2; cp->rw_latency = READ; break; case WRITE_10: sho = (short *) &cmd->cmnd[7]; x = ntohs(*sho)/2; cp->rw_latency = WRITE; break; case READ_10: sho = (short *) &cmd->cmnd[7]; x = ntohs(*sho)/2; cp->rw_latency = READ; break; case WRITE_12: lon = (long *) &cmd->cmnd[6]; x = ntohl(*lon)/2; cp->rw_latency = WRITE; break; case READ_12: lon = (long *) &cmd->cmnd[6]; x = ntohl(*lon)/2; cp->rw_latency = READ; break; default: cp->rw_latency = OTHER; break; } if (cmd->cmnd[0] == WRITE_6 || cmd->cmnd[0] == WRITE_10 || cmd->cmnd[0] == WRITE_12 || cmd->cmnd[0] == READ_6 || cmd->cmnd[0] == READ_10 || cmd->cmnd[0] == READ_12) { for(z = 0; (x > (1 << z)) && (z <= 11); z++) /* nothing */; cp->sizeindex = z; } }void eata_int_handler(int irq, void *dev_id, struct pt_regs * regs){ uint i, result = 0; uint hba_stat, scsi_stat, eata_stat; Scsi_Cmnd *cmd; struct eata_ccb *ccb; struct eata_sp *sp; uint base; uint x; struct Scsi_Host *sh; for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) { if (sh->irq != irq) continue; while(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ) { int_counter++; sp = &SD(sh)->sp;#ifdef __mips__ sys_cacheflush(sp, sizeof(struct eata_sp), 2);#endif ccb = sp->ccb; if(ccb == NULL) { eata_stat = inb((uint)sh->base + HA_RSTATUS); printk("eata_dma: int_handler, Spurious IRQ %d " "received. CCB pointer not set.\n", irq); break; } cmd = ccb->cmd; base = (uint) cmd->host->base; hba_stat = sp->hba_stat; scsi_stat = (sp->scsi_stat >> 1) & 0x1f; if (sp->EOC == FALSE) { eata_stat = inb(base + HA_RSTATUS); printk(KERN_WARNING "eata_dma: int_handler, board: %x cmd %lx " "returned unfinished.\n" "EATA: %x HBA: %x SCSI: %x spadr %lx spadrirq %lx, " "irq%d\n", base, (long)ccb, eata_stat, hba_stat, scsi_stat,(long)&status, (long)&status[irq], irq); cmd->result = DID_ERROR << 16; ccb->status = FREE; cmd->scsi_done(cmd); break; } sp->EOC = FALSE; /* Clean out this flag */ if (ccb->status == LOCKED || ccb->status == RESET) { printk("eata_dma: int_handler, reseted command pid %ld returned" "\n", cmd->pid); DBG(DBG_INTR && DBG_DELAY, DELAY(1)); } eata_stat = inb(base + HA_RSTATUS); DBG(DBG_INTR, printk("IRQ %d received, base %#.4x, pid %ld, " "target: %x, lun: %x, ea_s: %#.2x, hba_s: " "%#.2x \n", irq, base, cmd->pid, cmd->target, cmd->lun, eata_stat, hba_stat)); switch (hba_stat) { case HA_NO_ERROR: /* NO Error */ if(HD(cmd)->do_latency == TRUE && ccb->timestamp) eata_latency_in(ccb, HD(cmd)); result = DID_OK << 16; break; case HA_ERR_SEL_TO: /* Selection Timeout */ case HA_ERR_CMD_TO: /* Command Timeout */ result = DID_TIME_OUT << 16; break; case HA_BUS_RESET: /* SCSI Bus Reset Received */ result = DID_RESET << 16; DBG(DBG_STATUS, printk(KERN_WARNING "scsi%d: BUS RESET " "received on cmd %ld\n", HD(cmd)->HBA_number, cmd->pid)); break; case HA_INIT_POWERUP: /* Initial Controller Power-up */ if (cmd->device->type != TYPE_TAPE) result = DID_BUS_BUSY << 16; else result = DID_ERROR << 16; for (i = 0; i < MAXTARGET; i++) DBG(DBG_STATUS, printk(KERN_DEBUG "scsi%d: cmd pid %ld " "returned with INIT_POWERUP\n", HD(cmd)->HBA_number, cmd->pid)); break; case HA_CP_ABORT_NA: case HA_CP_ABORTED: result = DID_ABORT << 16; DBG(DBG_STATUS, printk(KERN_WARNING "scsi%d: aborted cmd " "returned\n", HD(cmd)->HBA_number)); break; case HA_CP_RESET_NA: case HA_CP_RESET: HD(cmd)->resetlevel[cmd->channel] = 0; result = DID_RESET << 16; DBG(DBG_STATUS, printk(KERN_WARNING "scsi%d: reseted cmd " "pid %ldreturned\n", HD(cmd)->HBA_number, cmd->pid)); case HA_SCSI_HUNG: /* SCSI Hung */ printk(KERN_ERR "scsi%d: SCSI hung\n", HD(cmd)->HBA_number); result = DID_ERROR << 16; break; case HA_RSENSE_FAIL: /* Auto Request-Sense Failed */ DBG(DBG_STATUS, printk(KERN_ERR "scsi%d: Auto Request Sense " "Failed\n", HD(cmd)->HBA_number)); result = DID_ERROR << 16; break; case HA_UNX_BUSPHASE: /* Unexpected Bus Phase */ case HA_UNX_BUS_FREE: /* Unexpected Bus Free */ case HA_BUS_PARITY: /* Bus Parity Error */ case HA_UNX_MSGRJCT: /* Unexpected Message Reject */ case HA_RESET_STUCK: /* SCSI Bus Reset Stuck */ case HA_PARITY_ERR: /* Controller Ram Parity */ default: result = DID_ERROR << 16; break; } cmd->result = result | (scsi_stat << 1); #if DBG_INTR2 if (scsi_stat || result || hba_stat || eata_stat != 0x50 || cmd->scsi_done == NULL || cmd->device->id == 7) printk("HBA: %d, channel %d, id: %d, lun %d, pid %ld:\n" "eata_stat %#x, hba_stat %#.2x, scsi_stat %#.2x, " "sense_key: %#x, result: %#.8x\n", x, cmd->device->channel, cmd->device->id, cmd->device->lun, cmd->pid, eata_stat, hba_stat, scsi_stat, cmd->sense_buffer[2] & 0xf, cmd->result); DBG(DBG_INTR&&DBG_DELAY,DELAY(1));#endif ccb->status = FREE; /* now we can release the slot */ cmd->scsi_done(cmd); } } return;}inline int eata_send_command(u32 addr, u32 base, u8 command){ long loop = R_LIMIT; while (inb(base + HA_RAUXSTAT) & HA_ABUSY) if (--loop == 0) return(FALSE); if(addr != (u32) NULL) addr = virt_to_bus((void *)addr); /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -