📄 atadrv.c
字号:
/* ataDrv.c - ATA/IDE (LOCAL and PCMCIA) disk device driver *//* Copyright 1989-1998 Wind River Systems, Inc. */#include "copyright_wrs.h"/*modification history--------------------01o,19sep99,jkf Removed extra SYS_ATA_INIT_RTN, now check wait in ataRW().01n,17jul99,jkf using words 60-61 to determine LBA instead of CHS calculation, for big drives, 8.4Gb+. SPR#22830.01m,04mar99,jkf Added SYS_ATA_INIT_RTN. _func_sysAtaInit. SPR#24378.01l,09jun98,dat fixed conflicting prototypes, removed ref to sysMsDelay()01k,09jun98,ms removed IMPORT prototypes conflicting with sysLib.h proto's01j,31mar98,map removed INCLUDE_ATA, redefined sys* prototypes.01i,23mar98,map renamed macros, made endian safe, added docs.01h,30oct97,db added cmd to reinitialize controller with params read. fixed bug reported in SPR #9139. used PCI macros for input/output.01g,21apr97,hdn fixed a semaphore timeout problem(SPR 8394).01f,28feb97,dat fixed SPRs 8084, 3273 from ideDrv.01e,06nov96,dgp doc: final formatting01d,01nov96,hdn added support for PCMCIA.01c,25sep96,hdn added support for ATA-2.01b,01mar96,hdn cleaned up.01a,02mar95,hdn written based on ideDrv.c.*//*DESCRIPTIONThis is a driver for ATA/IDE devices on PCMCIA, ISA, and other buses. Thedriver can be customized via various macros to run on a variety of boards andboth big-endian, and little endian CPUs.USER-CALLABLE ROUTINESMost of the routines in this driver are accessible only through the I/Osystem. However, two routines must be called directly: ataDrv() toinitialize the driver and ataDevCreate() to create devices.Before the driver can be used, it must be initialized by calling ataDrv().This routine must be called exactly once, before any reads, writes, orcalls to ataDevCreate(). Normally, it is called from usrRoot() inusrConfig.c.The routine ataRawio() supports physical I/O access. The firstargument is a drive number, 0 or 1; the second argument is a pointerto an ATA_RAW structure.NOTEFormat is not supported, because ATA/IDE disks are already formatted, and badsectors are mapped.During initialization this driver queries each disk to determine if the disk supports LBA. 16 bit words 0x60 and 0x61 (returned from the ATA IDENTIFY DEVICE command) may report a larger value than the product of the CHS fields on newer large disks (8.4Gb+). The driver will use strict LBA access commands and LBA geometry for drives reporting "total LBA sectors" greater than the product of CHS.Although everyone should also be using strict LBA on LBA disks, some older systems (mostly PC's) do not and use only CHS. Such system cannot view drives larger than 8GB. VxWorks does not have such limitations. However, it may be desirable to force VxWorks ignore the LBA information in favor of CHS in order to mount a file system originally formatted on a CHS only system. Setting the boolean ataForceCHSonLBA to TRUE will force the use of CHS parameters on all drives and the LBA parameters are ignored. Again, setting this boolean may prevent access to the drives full capacity, since some manufacturers have stopped setting a drives CHS accurately in favor of LBA.PARAMETERSThe ataDrv() function requires a configuration flag as a parameter.The configuration flag is one of the following:.TStab(|);l l .Transfer mode ATA_PIO_DEF_0 | PIO default modeATA_PIO_DEF_1 | PIO default mode, no IORDYATA_PIO_0 | PIO mode 0ATA_PIO_1 | PIO mode 1ATA_PIO_2 | PIO mode 2ATA_PIO_3 | PIO mode 3ATA_PIO_4 | PIO mode 4ATA_PIO_AUTO | PIO max supported modeATA_DMA_0 | DMA mode 0ATA_DMA_1 | DMA mode 1ATA_DMA_2 | DMA mode 2ATA_DMA_AUTO | DMA max supported modeTransfer bitsATA_BITS_16 | RW bits size, 16 bitsATA_BITS_32 | RW bits size, 32 bitsTransfer unitATA_PIO_SINGLE | RW PIO single sectorATA_PIO_MULTI | RW PIO multi sectorATA_DMA_SINGLE | RW DMA single wordATA_DMA_MULTI | RW DMA multi wordGeometry parametersATA_GEO_FORCE | set geometry in the tableATA_GEO_PHYSICAL | set physical geometryATA_GEO_CURRENT | set current geometry.TEDMA transfer is not supported in this release. If ATA_PIO_AUTO or ATA_DMA_AUTOis specified, the driver automatically chooses the maximum mode supported by thedevice. If ATA_PIO_MULTI or ATA_DMA_MULTI is specified, and the device doesnot support it, the driver automatically chooses single sector or word mode.If ATA_BITS_32 is specified, the driver uses 32-bit transfer mode regardless ofthe capability of the drive. If ATA_GEO_PHYSICAL is specified, the driver uses the physical geometry parameters stored in the drive. If ATA_GEO_CURRENT is specified,the driver uses current geometry parameters initialized by BIOS. If ATA_GEO_FORCE is specified, the driver uses geometry parameters stored in sysLib.c.The geometry parameters are stored in the structure table`ataTypes[]' in sysLib.c. That table has two entries, the first fordrive 0, the second for drive 1. The members of the structureare:.CS int cylinders; /@ number of cylinders @/ int heads; /@ number of heads @/ int sectors; /@ number of sectors per track @/ int bytes; /@ number of bytes per sector @/ int precomp; /@ precompensation cylinder @/.CEThis driver does not access the PCI-chip-set IDE interface, but rather takesadvantage of BIOS or VxWorks initialization. Thus, the BIOS setting should match the modes specified by the configuration flag.The BSP may provide a sysAtaInit() routine for situations where an ATAcontroller RESET (0x1f6 or 0x3f6, bit 2 is set) clears ATA specificfunctionality in a chipset that is not re-enabled per the ATA-2 spec. This BSP routine should be declared in sysLib.c or sysAta.c as follows: .CSvoid sysAtaInit (BOOL ctrl) { /@ BSP SPECIFIC CODE HERE @/ }.CEThen the BSP should perform the following operationbefore ataDrv() is called, in sysHwInit for example:.CS IMPORT VOIDFUNCPTR _func_sysAtaInit; /@ setup during initialization @/ _func_sysAtaInit = (VOIDFUNCPTR) sysAtaInit;.CE It should contain chipset specific reset code, such as code which re-enablesPCI write posting for an integrated PCI-IDE device, for example. This willbe executed during every ataDrv(), ataInit(), and ataReset() or equivalent block device routine. If the sysAtaInit routine is not provided by theBSP it is ignored by the driver, therefore it is not a required BSP routine.SEE ALSO:.pG "I/O System"*/#include "vxWorks.h"#include "taskLib.h"#include "ioLib.h"#include "memLib.h"#include "stdlib.h"#include "errnoLib.h"#include "stdio.h"#include "string.h"#include "private/semLibP.h"#include "intLib.h"#include "iv.h"#include "wdLib.h"#include "sysLib.h"#include "sys/fcntlcom.h"#include "drv/pcmcia/pcmciaLib.h"#include "drv/hdisk/ataDrv.h"#define VXDOS "VXDOS"#define VXEXT "VXEXT"/* imports */IMPORT ATA_TYPE ataTypes [ATA_MAX_CTRLS][ATA_MAX_DRIVES];IMPORT ATA_RESOURCE ataResources [ATA_MAX_CTRLS];/* Byte swapping version of sysInWordString(), big-endian CPUs only */IMPORT void sysInWordStringRev (int port, short *pData, int count);/* defines *//* Read a BYTE from IO port, `ioAdrs' */#ifndef ATA_IO_BYTE_READ#define ATA_IO_BYTE_READ(ioAdrs) sysInByte (ioAdrs)#endif /* ATA_IO_BYTE_READ *//* Write a BYTE `byte' to IO port, `ioAdrs' */#ifndef ATA_IO_BYTE_WRITE#define ATA_IO_BYTE_WRITE(ioAdrs, byte) sysOutByte (ioAdrs, byte)#endif /* ATA_IO_BYTE_WRITE *//* Read 16-bit little-endian `nWords' into `pData' from IO port, `ioAdrs' */#ifndef ATA_IO_NWORD_READ#define ATA_IO_NWORD_READ(ioAdrs, pData, nWords) \ sysInWordString (ioAdrs, pData, nWords)#endif /* ATA_IO_NWORD_READ *//* Write 16-bit little-endian `nWords' from `pData' into IO port, `ioAdrs' */#ifndef ATA_IO_NWORD_WRITE#define ATA_IO_NWORD_WRITE(ioAdrs, pData, nWords) \ sysOutWordString (ioAdrs, pData, nWords)#endif /* ATA_IO_NWORD_WRITE */ /* Read 32-bit little-endian `nLongs' into `pData' from IO port, `ioAdrs' */#ifndef ATA_IO_NLONG_READ#define ATA_IO_NLONG_READ(ioAdrs, pData, nLongs) \ sysInLongString (ioAdrs, pData, nLongs)#endif /* ATA_IO_NLONG_READ *//* Write 32-bit little-endian `nLongs' from `pData' into IO port, `ioAdrs' */#ifndef ATA_IO_NLONG_WRITE#define ATA_IO_NLONG_WRITE(ioAdrs, pData, nLongs) \ sysOutLongString (ioAdrs, pData, nLongs)#endif /* ATA_IO_NLONG_WRITE *//* Read 32-bit CPU-endian `nWords' into `pData' from IO port, `ioAdrs' */#ifndef ATA_IO_NWORD_READ_SWAP# if (_BYTE_ORDER == _BIG_ENDIAN)# define ATA_IO_NWORD_READ_SWAP(ioAdrs, pData, nWords) \ sysInWordStringRev (ioAdrs, pData, nWords)# else /* (_BYTE_ORDER == _BIG_ENDIAN) */# define ATA_IO_NWORD_READ_SWAP(ioAdrs, pData, nWords) \ ATA_IO_NWORD_READ (ioAdrs, pData, nWords)# endif /* (_BYTE_ORDER == _BIG_ENDIAN) */#endif /* ATA_IO_NLONG_READ_SWAP *//* Special BSP INIT After ATA Reset *//* Special BSP INIT After ATA Reset */#ifndef SYS_ATA_INIT_RTN#define SYS_ATA_INIT_RTN(ctrl) if (_func_sysAtaInit != NULL) \ { \ ((*_func_sysAtaInit)(ctrl)); \ }#endif/* globals */ BOOL ataDrvInstalled = FALSE; /* TRUE if installed */BOOL ataForceCHSonLBA = FALSE; /* hack, forces use of CHS params */ATA_CTRL ataCtrl [ATA_MAX_CTRLS];/* BSP specific ATA Init/Reset routine */VOIDFUNCPTR _func_sysAtaInit = NULL;/* locals */LOCAL int ataRetry = 3; /* max retry count *//* Used to hold LBA information, if larger than calculated CHS value */LOCAL UINT32 ataLbaTotalSecs [ATA_MAX_CTRLS][ATA_MAX_DRIVES];/* function prototypes */LOCAL STATUS ataBlkRd (ATA_DEV *pDev, int startBlk, int nBlks, char *p);LOCAL STATUS ataBlkWrt (ATA_DEV *pDev, int startBlk, int nBlks, char *p);LOCAL STATUS ataReset (ATA_DEV *pDev);LOCAL STATUS ataStatus (ATA_DEV *pDev);LOCAL STATUS ataIoctl (ATA_DEV *pDev, int function, int arg);LOCAL STATUS ataBlkRW (ATA_DEV *pDev, int startBlk, int nBlks, char *p, int direction);LOCAL void ataWdog (int ctrl);LOCAL void ataIntr (int ctrl);LOCAL STATUS ataInit (int ctrl);LOCAL void ataWait (int ctrl, int request);LOCAL STATUS ataCmd (int ctrl, int drive, int cmd, int arg0, int arg1);LOCAL STATUS ataPread (int ctrl, int drive, void *p);LOCAL STATUS ataRW (int ctrl, int drive, int cylinder, int head, int sec, void *p, int nSecs, int direction);/********************************************************************************* ataDrv - initialize the ATA driver** This routine initializes the ATA/IDE driver, sets up interrupt vectors,* and performs hardware initialization of the ATA/IDE chip.** This routine must be called exactly once, before any reads, writes,* or calls to ataDevCreate(). Normally, it is called by usrRoot()* in usrConfig.c.** RETURNS: OK, or ERROR if initialization fails.** SEE ALSO: ataDevCreate()*/STATUS ataDrv ( int ctrl, /* controller no. */ int drives, /* number of drives */ int vector, /* interrupt vector */ int level, /* interrupt level */ BOOL configType, /* configuration type */ int semTimeout, /* timeout seconds for sync semaphore */ int wdgTimeout /* timeout seconds for watch dog */ ) { ATA_CTRL *pCtrl = &ataCtrl[ctrl]; ATA_RESOURCE *pAta = &ataResources[ctrl]; PCCARD_RESOURCE *pResource = &pAta->resource; ATA_DRIVE *pDrive; ATA_PARAM *pParam; ATA_TYPE *pType; int drive; int ix; if ((ctrl >= ATA_MAX_CTRLS) || (drives > ATA_MAX_DRIVES)) return (ERROR); if (!ataDrvInstalled) { for (ix = 0; ix < ATA_MAX_CTRLS; ix++) ataCtrl[ix].wdgId = wdCreate (); ataDrvInstalled = TRUE; } if (!pCtrl->installed) { if (semTimeout == 0) pCtrl->semTimeout = ATA_SEM_TIMEOUT_DEF; else pCtrl->semTimeout = semTimeout; if (wdgTimeout == 0) pCtrl->wdgTimeout = ATA_WDG_TIMEOUT_DEF; else pCtrl->wdgTimeout = wdgTimeout; semBInit (&pCtrl->syncSem, SEM_Q_FIFO, SEM_EMPTY); semMInit (&pCtrl->muteSem, SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE); pCtrl->data = ATA_DATA (pResource->ioStart[0]); pCtrl->error = ATA_ERROR (pResource->ioStart[0]); pCtrl->feature = ATA_FEATURE (pResource->ioStart[0]); pCtrl->seccnt = ATA_SECCNT (pResource->ioStart[0]); pCtrl->sector = ATA_SECTOR (pResource->ioStart[0]); pCtrl->cylLo = ATA_CYL_LO (pResource->ioStart[0]); pCtrl->cylHi = ATA_CYL_HI (pResource->ioStart[0]); pCtrl->sdh = ATA_SDH (pResource->ioStart[0]); pCtrl->command = ATA_COMMAND (pResource->ioStart[0]); pCtrl->status = ATA_STATUS (pResource->ioStart[0]); pCtrl->aStatus = ATA_A_STATUS (pResource->ioStart[1]); pCtrl->dControl = ATA_D_CONTROL (pResource->ioStart[1]); pCtrl->dAddress = ATA_D_ADDRESS (pResource->ioStart[1]); (void) intConnect ((VOIDFUNCPTR *)INUM_TO_IVEC (vector), (VOIDFUNCPTR)ataIntr, ctrl); sysIntEnablePIC (level); /* unmask the interrupt level */ pCtrl->intLevel = level; pCtrl->wdgOkay = TRUE; semTake (&pCtrl->muteSem, WAIT_FOREVER); if (ataInit (ctrl) != OK) { semGive (&pCtrl->muteSem); return (ERROR); } for (drive = 0; drive < drives; drive++) { pType = &ataTypes[ctrl][drive]; pDrive = &pCtrl->drive[drive]; pParam = &pDrive->param; if (pType->cylinders == 0) break; if ((pCtrl->ctrlType == ATA_PCMCIA) || ((pCtrl->ctrlType != ATA_PCMCIA) && (drive == 0))) { if (ataCmd (ctrl, drive, ATA_CMD_DIAGNOSE, NULL, NULL) != OK) { semGive (&pCtrl->muteSem); return (ERROR); } } /* find out geometry */ if ((configType & ATA_GEO_MASK) == ATA_GEO_FORCE) { (void) ataCmd (ctrl, drive, ATA_CMD_INITP, NULL, NULL); (void) ataPread (ctrl, drive, (char *)pParam); } else if ((configType & ATA_GEO_MASK) == ATA_GEO_PHYSICAL) { (void) ataPread (ctrl, drive, (char *)pParam); pType->cylinders = pParam->cylinders - 1; pType->heads = pParam->heads; pType->sectors = pParam->sectors; } else if ((configType & ATA_GEO_MASK) == ATA_GEO_CURRENT) { (void) ataPread (ctrl, drive, (char *)pParam); if ((pParam->currentCylinders != 0) && (pParam->currentHeads != 0) && (pParam->currentSectors != 0)) { pType->cylinders = pParam->currentCylinders - 1; pType->heads = pParam->currentHeads; pType->sectors = pParam->currentSectors; } else { pType->cylinders = pParam->cylinders - 1; pType->heads = pParam->heads; pType->sectors = pParam->sectors; } } /* * Not all modern hard drives report a true capacity value * in their IDENTIFY DEVICE CHS fields. * For example, a Western Digital 20 Gb drive reports * its CHS as 16383 cylinders, 16 heads, and 63 spt. * This is about 8.4GB, but the LBA sectors is reported * as 0x02607780, which is closer to 20Gb, the true capacity * of the drive. The reason for this is PC BIOS can have a * 8.4GB limitation, and drive manufacturers have broken the * ATA specification to be compatable. Negative competition. * Note that the ATA specifications original limit is * about 136.9 Gb, however when combinined with a PC BIOS * interface, a 8.4 Gb limit is produced. * VxWorks does not have such limitations being a true 32bit OS, * but since the drive manufactures are not honoring the CHS * values, we have to allow for devices that demand "pure" LBA * and present incorrect CHS. * If the drive supports Logical Block Addresses (LBA) * then we need to check the field located at 16bit words 60 & 61, * "Total number of user addressable sectors (LBA mode only)". * If this value is greater than the CHS fields report, * then 60-61 holds the true size of the disk and that * will be reported to the block device interface. * Note that the CHS values are still left as the disk reported. * This is tracked at WRS as SPR#22830 */ if (pParam->capabilities & 0x0200) /* if (drive supports LBA) */ { ataLbaTotalSecs[ctrl][drive] = (UINT32) ((((UINT32) ((pParam->sectors0) & 0x0000ffff)) << 0) | (((UINT32) ((pParam->sectors1) & 0x0000ffff)) << 16));#ifdef ATA_DEBUG printf ("ID_DRIVE reports LBA (60-61) as 0x%08lx\n", ataLbaTotalSecs[ctrl][drive]);#endif /* ATA_DEBUG */ } /* * reinitialize the controller with parameters read from the * controller. */ (void) ataCmd (ctrl, drive, ATA_CMD_INITP, NULL, NULL); /* recalibrate */ (void) ataCmd (ctrl, drive, ATA_CMD_RECALIB, NULL, NULL); /* find out supported capabilities of the drive */ pDrive->multiSecs = pParam->multiSecs & 0x00ff; pDrive->okMulti = (pDrive->multiSecs != 0) ? TRUE : FALSE; pDrive->okIordy = (pParam->capabilities & 0x0800) ? TRUE : FALSE; pDrive->okLba = (pParam->capabilities & 0x0200) ? TRUE : FALSE; pDrive->okDma = (pParam->capabilities & 0x0100) ? TRUE : FALSE; /* find out supported max PIO mode */ pDrive->pioMode = (pParam->pioMode >> 8) & 0x03; /* PIO 0,1,2 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -