📄 psi240i.c
字号:
/*+M************************************************************************* * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux. * * Copyright (c) 1997 Perceptive Solutions, Inc. * * 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, 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 program; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * * File Name: psi240i.c * * Description: SCSI driver for the PSI240I EIDE interface card. * *-M*************************************************************************/#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/proc_fs.h>#include <linux/spinlock.h>#include <asm/dma.h>#include <asm/system.h>#include <asm/io.h>#include <linux/blk.h>#include "scsi.h"#include "hosts.h"#include "psi240i.h"#include "psi_chip.h"#include<linux/stat.h>//#define DEBUG 1#ifdef DEBUG#define DEB(x) x#else#define DEB(x)#endif#define MAXBOARDS 2 /* Increase this and the sizes of the arrays below, if you need more. */#define PORT_DATA 0#define PORT_ERROR 1#define PORT_SECTOR_COUNT 2#define PORT_LBA_0 3#define PORT_LBA_8 4#define PORT_LBA_16 5#define PORT_LBA_24 6#define PORT_STAT_CMD 7#define PORT_SEL_FAIL 8#define PORT_IRQ_STATUS 9#define PORT_ADDRESS 10#define PORT_FAIL 11#define PORT_ALT_STAT 12typedef struct { UCHAR device; // device code UCHAR byte6; // device select register image UCHAR spigot; // spigot number UCHAR expectingIRQ; // flag for expecting and interrupt USHORT sectors; // number of sectors per track USHORT heads; // number of heads USHORT cylinders; // number of cylinders for this device USHORT spareword; // placeholder ULONG blocks; // number of blocks on device } OUR_DEVICE, *POUR_DEVICE;typedef struct { USHORT ports[13]; OUR_DEVICE device[8]; Scsi_Cmnd *pSCmnd; IDE_STRUCT ide; ULONG startSector; USHORT sectorCount; Scsi_Cmnd *SCpnt; VOID *buffer; USHORT expectingIRQ; } ADAPTER240I, *PADAPTER240I;#define HOSTDATA(host) ((PADAPTER240I)&host->hostdata)static struct Scsi_Host *PsiHost[6] = {NULL,}; /* One for each IRQ level (10-15) */static IDENTIFY_DATA identifyData;static SETUP ChipSetup;static USHORT portAddr[6] = {CHIP_ADRS_0, CHIP_ADRS_1, CHIP_ADRS_2, CHIP_ADRS_3, CHIP_ADRS_4, CHIP_ADRS_5};/**************************************************************** * Name: WriteData :LOCAL * * Description: Write data to device. * * Parameters: padapter - Pointer adapter data structure. * * Returns: TRUE if drive does not assert DRQ in time. * ****************************************************************/static int WriteData (PADAPTER240I padapter) { ULONG timer; USHORT *pports = padapter->ports; timer = jiffies + TIMEOUT_DRQ; // calculate the timeout value do { if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ ) { outsw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ide[2] * 256); return 0; } } while ( time_after(timer, jiffies) ); // test for timeout padapter->ide.ide.ides.cmd = 0; // null out the command byte return 1; }/**************************************************************** * Name: IdeCmd :LOCAL * * Description: Process a queued command from the SCSI manager. * * Parameters: padapter - Pointer adapter data structure. * * Returns: Zero if no error or status register contents on error. * ****************************************************************/static UCHAR IdeCmd (PADAPTER240I padapter) { ULONG timer; USHORT *pports = padapter->ports; UCHAR status; outb_p (padapter->ide.ide.ides.spigot, pports[PORT_SEL_FAIL]); // select the spigot outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]); // select the drive timer = jiffies + TIMEOUT_READY; // calculate the timeout value do { status = inb_p (padapter->ports[PORT_STAT_CMD]); if ( status & IDE_STATUS_DRDY ) { outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]); outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]); outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]); outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]); padapter->expectingIRQ = 1; outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]); if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE ) return (WriteData (padapter)); return 0; } } while ( time_after(timer, jiffies) ); // test for timeout padapter->ide.ide.ides.cmd = 0; // null out the command byte return status; }/**************************************************************** * Name: SetupTransfer :LOCAL * * Description: Setup a data transfer command. * * Parameters: padapter - Pointer adapter data structure. * drive - Drive/head register upper nibble only. * * Returns: TRUE if no data to transfer. * ****************************************************************/static int SetupTransfer (PADAPTER240I padapter, UCHAR drive) { if ( padapter->sectorCount ) { *(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector; padapter->ide.ide.ide[6] |= drive; padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount; padapter->sectorCount -= padapter->ide.ide.ides.sectors; // bump the start and count for next xfer padapter->startSector += padapter->ide.ide.ides.sectors; return 0; } else { padapter->ide.ide.ides.cmd = 0; // null out the command byte padapter->SCpnt = NULL; return 1; } }/**************************************************************** * Name: DecodeError :LOCAL * * Description: Decode and process device errors. * * Parameters: pshost - Pointer to host data block. * status - Status register code. * * Returns: The driver status code. * ****************************************************************/static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status) { PADAPTER240I padapter = HOSTDATA(pshost); UCHAR error; padapter->expectingIRQ = 0; padapter->SCpnt = NULL; if ( status & IDE_STATUS_WRITE_FAULT ) { return DID_PARITY << 16; } if ( status & IDE_STATUS_BUSY ) return DID_BUS_BUSY << 16; error = inb_p (padapter->ports[PORT_ERROR]); DEB(printk ("\npsi240i error register: %x", error)); switch ( error ) { case IDE_ERROR_AMNF: case IDE_ERROR_TKONF: case IDE_ERROR_ABRT: case IDE_ERROR_IDFN: case IDE_ERROR_UNC: case IDE_ERROR_BBK: default: return DID_ERROR << 16; } return DID_ERROR << 16; }/**************************************************************** * Name: Irq_Handler :LOCAL * * Description: Interrupt handler. * * Parameters: irq - Hardware IRQ number. * dev_id - * regs - * * Returns: TRUE if drive is not ready in time. * ****************************************************************/static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs) { struct Scsi_Host *shost; // Pointer to host data block PADAPTER240I padapter; // Pointer to adapter control structure USHORT *pports; // I/O port array Scsi_Cmnd *SCpnt; UCHAR status; int z; DEB(printk ("\npsi240i received interrupt\n")); shost = PsiHost[irq - 10]; if ( !shost ) panic ("Splunge!"); padapter = HOSTDATA(shost); pports = padapter->ports; SCpnt = padapter->SCpnt; if ( !padapter->expectingIRQ ) { DEB(printk ("\npsi240i Unsolicited interrupt\n")); return; } padapter->expectingIRQ = 0; status = inb_p (padapter->ports[PORT_STAT_CMD]); // read the device status if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) ) goto irqerror; DEB(printk ("\npsi240i processing interrupt")); switch ( padapter->ide.ide.ides.cmd ) // decide how to handle the interrupt { case IDE_CMD_READ_MULTIPLE: if ( status & IDE_STATUS_DRQ ) { insw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ides.sectors * 256); padapter->buffer += padapter->ide.ide.ides.sectors * 512; if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) ) { SCpnt->result = DID_OK << 16; padapter->SCpnt = NULL; SCpnt->scsi_done (SCpnt); return; } if ( !(status = IdeCmd (padapter)) ) return; } break; case IDE_CMD_WRITE_MULTIPLE: padapter->buffer += padapter->ide.ide.ides.sectors * 512; if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) ) { SCpnt->result = DID_OK << 16; padapter->SCpnt = NULL; SCpnt->scsi_done (SCpnt); return; } if ( !(status = IdeCmd (padapter)) ) return; break; case IDE_COMMAND_IDENTIFY: { PINQUIRYDATA pinquiryData = SCpnt->request_buffer; if ( status & IDE_STATUS_DRQ ) { insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1); memset (pinquiryData, 0, SCpnt->request_bufflen); // Zero INQUIRY data structure. pinquiryData->DeviceType = 0; pinquiryData->Versions = 2; pinquiryData->AdditionalLength = 35 - 4; // Fill in vendor identification fields. for ( z = 0; z < 20; z += 2 ) { pinquiryData->VendorId[z] = ((UCHAR *)identifyData.ModelNumber)[z + 1]; pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z]; } // Initialize unused portion of product id. for ( z = 0; z < 4; z++ ) pinquiryData->ProductId[12 + z] = ' '; // Move firmware revision from IDENTIFY data to // product revision in INQUIRY data. for ( z = 0; z < 4; z += 2 ) { pinquiryData->ProductRevisionLevel[z] = ((UCHAR *)identifyData.FirmwareRevision)[z + 1]; pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z]; } SCpnt->result = DID_OK << 16; padapter->SCpnt = NULL; SCpnt->scsi_done (SCpnt); return; } break; } default: SCpnt->result = DID_OK << 16; padapter->SCpnt = NULL; SCpnt->scsi_done (SCpnt); return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -