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