/* $Id: atalib.c 74 2015-12-21 00:40:51Z bird $ */ /** @file * Does a little write test. */ /* * Copyright (c) 2015 knut st. osmundsen * * 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 3 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 program. If not, see * */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #include #include #include #include #include #include #include #include "atalib.h" #define STR_TUPLE(a_szStr) a_szStr, sizeof(a_szStr) - 1 /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ uint16_t g_uBasePort; uint16_t g_uCtrlPort; uint8_t g_cPortShift; uint8_t g_bDevice; uint8_t g_fUseLbaMode = 1; uint8_t g_f8BitData; uint8_t g_fSupportsSetFeature8BitData; uint8_t g_fSupportsSetFeatureWriteCache; uint8_t g_fSupportsSetFeatureXferMode; int8_t g_fSupportsReadBuffer = -1; int8_t g_fSupportsWriteBuffer = -1; uint16_t g_cHeads; uint8_t g_cSectorsPerTrack; uint16_t g_cCylinders; uint16_t g_cSectorsPerCylinder; /** The result of the identify command. */ uint16_t g_awIdentify[256]; size_t AtaPrintStatus(FILE *pOut, uint8_t bSts) { size_t cch = fprintf(pOut, "%#x", bSts); if (bSts & ATA_STS_BUSY) cch += fprintf(pOut, " busy"); if (bSts & ATA_STS_DRDY) cch += fprintf(pOut, " drdy"); if (bSts & ATA_STS_DF ) cch += fprintf(pOut, " df"); if (bSts & ATA_STS_DSC ) cch += fprintf(pOut, " dsc"); if (bSts & ATA_STS_DRQ ) cch += fprintf(pOut, " drq"); if (bSts & ATA_STS_CORR) cch += fprintf(pOut, " corr"); if (bSts & ATA_STS_IDX ) cch += fprintf(pOut, " idx"); if (bSts & ATA_STS_ERR ) cch += fprintf(pOut, " err"); return cch; } size_t AtaPrintError(FILE *pOut, uint8_t bErr) { size_t cch = fprintf(pOut, "%#x", bErr); if (bErr & ATA_ERR_RSVR ) cch += fprintf(pOut, " rsrv"); if (bErr & ATA_ERR_UNC ) cch += fprintf(pOut, " unc"); if (bErr & ATA_ERR_MC ) cch += fprintf(pOut, " mc"); if (bErr & ATA_ERR_IDNF ) cch += fprintf(pOut, " idnf"); if (bErr & ATA_ERR_MCR ) cch += fprintf(pOut, " mcr"); if (bErr & ATA_ERR_ABRT ) cch += fprintf(pOut, " abrt"); if (bErr & ATA_ERR_TKNONF) cch += fprintf(pOut, " tknonf"); if (bErr & ATA_ERR_AMNF ) cch += fprintf(pOut, " amnf"); return cch; } static int AtaError(uint8_t bSts, const char *pszFormat, ...) { va_list va; fprintf(stderr, "error: "); va_start(va, pszFormat); vfprintf(stderr, pszFormat, va); va_end(va); fprintf(stderr, "\n status="); AtaPrintStatus(stderr, bSts); fprintf(stderr, "\n error= "); AtaPrintError(stderr, inp(ATA_REG_ERROR(g_uBasePort))); fprintf(stderr, "\n"); return -1; } uint8_t AtaWaitBusy(void) { uint32_t cLoops = 0; uint8_t bStatus; do { if ((++cLoops & 0xfffff) == 0) fprintf(stderr, "AtaWaitBusyForData: cLoops=%#lx\n", cLoops); bStatus = inp(ATA_REG_STATUS(g_uBasePort)); } while ((bStatus & (ATA_STS_BUSY | ATA_STS_ERR)) == ATA_STS_BUSY); return bStatus; } uint8_t AtaWaitBusyDeviceReady(void) { uint32_t cLoops = 0; uint8_t bStatus; do { if ((++cLoops & 0xfffff) == 0) fprintf(stderr, "AtaWaitBusyForData: cLoops=%#lx\n", cLoops); bStatus = inp(ATA_REG_STATUS(g_uBasePort)); } while ( (bStatus & (ATA_STS_BUSY | ATA_STS_DRDY)) != ATA_STS_DRDY && !(bStatus & ATA_STS_ERR) ); return bStatus; } uint8_t AtaWaitBusyForData(void) { uint32_t cLoops = 0; uint8_t bStatus; do { if ((++cLoops & 0xfffff) == 0) fprintf(stderr, "AtaWaitBusyForData: cLoops=%#lx\n", cLoops); bStatus = inp(ATA_REG_STATUS(g_uBasePort)); } while ( (bStatus & (ATA_STS_BUSY | ATA_STS_DRQ)) != ATA_STS_DRQ && !(bStatus & ATA_STS_ERR) ); return bStatus; } uint8_t AtaSubmitCommandAndWait(uint8_t bCommand) { outp(ATA_REG_COMMAND(g_uBasePort), bCommand); ATA_DELAY_400NS(); return AtaWaitBusy(); } uint8_t AtaSubmitCommandAndWaitForData(uint8_t bCommand) { outp(ATA_REG_COMMAND(g_uBasePort), bCommand); ATA_DELAY_400NS(); return AtaWaitBusyForData(); } uint8_t AtaSelectDevice(uint8_t bDevice) { outp(ATA_REG_DEVICE_SELECT(g_uBasePort), g_bDevice); ATA_DELAY_400NS(); return AtaWaitBusyDeviceReady(); } void AtaSetSectorAddress(uint32_t iSector, uint8_t bDevice) { if (g_fUseLbaMode) { outp(ATA_REG_LBA_0_7(g_uBasePort), iSector & 0xff); outp(ATA_REG_LBA_8_15(g_uBasePort), (iSector >> 8) & 0xff); outp(ATA_REG_LBA_16_23(g_uBasePort), (iSector >> 16) & 0xff); outp(ATA_REG_LBA_24_27_MODE(g_uBasePort), ((iSector >> 24) & 0x0f) | ATA_LBA_MODE | bDevice); } else { uint16_t iCyl = iSector / g_cSectorsPerCylinder; uint16_t iRem = iSector % g_cSectorsPerCylinder; uint8_t iHd = iRem / g_cSectorsPerTrack; uint8_t iSec = iRem % g_cSectorsPerTrack; outp(ATA_REG_SECTOR_NUMBER(g_uBasePort), iSec); outp(ATA_REG_CYLINDER_LOW(g_uBasePort), iCyl & 0xff); outp(ATA_REG_CYLINDER_HIGH(g_uBasePort), iCyl >> 8); outp(ATA_REG_HEAD(g_uBasePort), iHd | bDevice); } } void AtaReadData(void *pvBuf, size_t cb, uint8_t f8BitData) { uint16_t uDataPort = ATA_REG_DATA(g_uBasePort); uint16_t *pu16 = (uint16_t *)pvBuf; cb >>= 1; if (f8BitData == 1) { while (cb-- > 0) { uint8_t b1 = inp(uDataPort); uint8_t b2 = inp(uDataPort); *pu16++ = b1 | ((uint16_t)b2 << 8); } } else { while (cb-- > 0) *pu16++ = inpw(uDataPort); } } void AtaWriteData(void const *pvBuf, size_t cb, uint8_t f8BitData) { uint16_t register uDataPort = ATA_REG_DATA(g_uBasePort); uint16_t const *pu16 = (uint16_t const *)pvBuf; cb >>= 1; if (f8BitData == 1) { while (cb-- > 0) { uint16_t register u16 = *pu16++; outp(uDataPort, (uint8_t)u16); outp(uDataPort, (uint8_t)(u16 >> 8)); } } else while (cb-- > 0) outpw(uDataPort, *pu16++); } int AtaReadSector(uint32_t iSector, void *pvBuf) { uint8_t bSts = AtaWaitBusy(); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Prepping for reading sector %lu", iSector); bSts = AtaSelectDevice(g_bDevice); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Selecting device for reading sector %lu", iSector); outp(ATA_REG_FEATURES(g_uBasePort), 0x0); outp(ATA_REG_SECTOR_COUNT(g_uBasePort), 1); AtaSetSectorAddress(iSector, g_bDevice); bSts = AtaSubmitCommandAndWaitForData(ATA_CMD_READ_SECTORS); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Reading sector %lu", iSector); if (!(bSts & ATA_STS_DRQ)) return AtaError(bSts, "DRQ not set after reading sector %lu", iSector); AtaReadData(pvBuf, 512, g_f8BitData); bSts = inp(ATA_REG_STATUS(g_uBasePort)); if (bSts & ATA_STS_DRQ) return AtaError(bSts, "DRQ is still set after reading sector %lu", iSector); if (bSts & ATA_STS_ERR) return AtaError(bSts, "ERR is set after reading sector %lu (#2)", iSector); return 0; } int AtaWriteSector(uint32_t iSector, void const *pvBuf) { uint8_t bSts = AtaWaitBusy(); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Prepping for writing sector %lu", iSector); bSts = AtaSelectDevice(g_bDevice); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Selecting device for writing sector %lu", iSector); outp(ATA_REG_FEATURES(g_uBasePort), 0x0); outp(ATA_REG_SECTOR_COUNT(g_uBasePort), 1); AtaSetSectorAddress(iSector, g_bDevice); bSts = AtaSubmitCommandAndWaitForData(ATA_CMD_WRITE_SECTORS); if (bSts & ATA_STS_ERR) return AtaError(bSts, "writing sector (#1) %lu", iSector); if (!(bSts & ATA_STS_DRQ)) return AtaError(bSts, "DRQ not set after writing sector (#1) %lu", iSector); AtaWriteData(pvBuf, 512, g_f8BitData); ATA_DELAY_400NS(); bSts = AtaWaitBusy(); if (bSts & ATA_STS_ERR) return AtaError(bSts, "writing sector (#2) %lu", iSector); if (bSts & ATA_STS_DRQ) return AtaError(bSts, "DRQ is set after writing sector (#2) %lu", iSector); return 0; } /** * @param pvBuf Pointer to a 512-byte buffer. */ int AtaReadBuffer(void *pvBuf, uint8_t fExtraChecks) { uint8_t bSts; if (!g_fSupportsReadBuffer) return -2; bSts = AtaWaitBusy(); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Prepping for reading buffer"); bSts = AtaSelectDevice(g_bDevice); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Selecting device for reading buffer"); outp(ATA_REG_FEATURES(g_uBasePort), 0); outp(ATA_REG_SECTOR_COUNT(g_uBasePort), 1); /* ignored */ outp(ATA_REG_SECTOR_NUMBER(g_uBasePort), 0); outp(ATA_REG_CYLINDER_LOW(g_uBasePort), 0); outp(ATA_REG_CYLINDER_HIGH(g_uBasePort), 0); bSts = AtaSubmitCommandAndWaitForData(ATA_CMD_READ_BUFFER); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Reading buffer"); if (!(bSts & ATA_STS_DRQ)) return AtaError(bSts, "DRQ not set after reading buffer"); if (!fExtraChecks) AtaReadData(pvBuf, 512, g_f8BitData); else AtaReadData(pvBuf, 512, g_f8BitData); bSts = inp(ATA_REG_STATUS(g_uBasePort)); if (bSts & ATA_STS_DRQ) return AtaError(bSts, "DRQ is still set after reading buffer"); if (bSts & ATA_STS_ERR) return AtaError(bSts, "ERR is set after reading buffer"); return 0; } int AtaWriteBuffer(void const *pvBuf, uint8_t fExtraChecks) { uint8_t bSts; if (!g_fSupportsWriteBuffer) return -2; bSts = AtaWaitBusy(); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Prepping for writing buffer"); bSts = AtaSelectDevice(g_bDevice); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Selecting device for writing buffer"); outp(ATA_REG_FEATURES(g_uBasePort), 0); outp(ATA_REG_SECTOR_COUNT(g_uBasePort), 1); /* ignored */ outp(ATA_REG_SECTOR_NUMBER(g_uBasePort), 0); outp(ATA_REG_CYLINDER_LOW(g_uBasePort), 0); outp(ATA_REG_CYLINDER_HIGH(g_uBasePort), 0); bSts = AtaSubmitCommandAndWaitForData(ATA_CMD_WRITE_BUFFER); if (bSts & ATA_STS_ERR) return AtaError(bSts, "writing buffer (#1)"); if (!(bSts & ATA_STS_DRQ)) return AtaError(bSts, "DRQ not set after writing buffer (#1)"); if (!fExtraChecks) AtaWriteData(pvBuf, 512, g_f8BitData); else AtaWriteData(pvBuf, 512, g_f8BitData); ATA_DELAY_400NS(); bSts = AtaWaitBusy(); if (bSts & ATA_STS_ERR) return AtaError(bSts, "writing buffer (#2)"); if (bSts & ATA_STS_DRQ) return AtaError(bSts, "DRQ is set after writing buffer (#2)"); return 0; } int AtaIdentifyDevice(uint8_t bDevice, void *pvBuf) { uint8_t bSts = AtaWaitBusy(); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Prepping for device %#x identification", bDevice); bSts = AtaSelectDevice(g_bDevice); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Selecting device %#x for identification", bDevice); outp(ATA_REG_FEATURES(g_uBasePort), 0); outp(ATA_REG_SECTOR_COUNT(g_uBasePort), 0); outp(ATA_REG_SECTOR_NUMBER(g_uBasePort), 0); outp(ATA_REG_CYLINDER_LOW(g_uBasePort), 0); outp(ATA_REG_CYLINDER_HIGH(g_uBasePort), 0); //outp(ATA_REG_HEAD(g_uBasePort), g_bDevice); bSts = AtaSubmitCommandAndWaitForData(ATA_CMD_IDENTIFY_DEVICE); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Device %#x identification", bDevice); if (!(bSts & ATA_STS_DRQ)) return AtaError(bSts, "DRQ not set after device %#x identification", bDevice); AtaReadData(pvBuf, 512, g_f8BitData); return 0; } int AtaSetFeature(uint8_t bDevice, uint8_t bFeature, uint8_t bValue) { uint8_t bSts = AtaWaitBusy(); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Prepping for setting device %#x feature %#x (%#x)", bDevice, bFeature, bValue); bSts = AtaSelectDevice(g_bDevice); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Selecting device %#x for setting feature %#x (%#x)", bDevice, bFeature, bValue); outp(ATA_REG_FEATURES(g_uBasePort), bFeature); outp(ATA_REG_SECTOR_COUNT(g_uBasePort), 0); outp(ATA_REG_SECTOR_NUMBER(g_uBasePort), bValue); outp(ATA_REG_CYLINDER_LOW(g_uBasePort), 0); outp(ATA_REG_CYLINDER_HIGH(g_uBasePort), 0); //outp(ATA_REG_HEAD(g_uBasePort), g_bDevice); bSts = AtaSubmitCommandAndWait(ATA_CMD_SET_FEATURES); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Setting device %#x feature %#x (%#x)", bDevice, bFeature, bValue); if (bSts & ATA_STS_DRQ) return AtaError(bSts, "DRQ is set after setting device %#x feature %#x (%#x)", bDevice, bFeature, bValue); return 0; } int AtaInitDeviceParams(uint8_t bDevice, uint8_t cSectorsPerTrack, uint8_t cHeads) { uint8_t bSts = AtaWaitBusy(); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Prepping for device %#x parameter initialization", bDevice); bSts = AtaSelectDevice(g_bDevice); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Selecting device for device %#x parameter initialization", bDevice); outp(ATA_REG_FEATURES(g_uBasePort), 0); outp(ATA_REG_SECTOR_COUNT(g_uBasePort), cSectorsPerTrack); outp(ATA_REG_SECTOR_NUMBER(g_uBasePort), 0); outp(ATA_REG_CYLINDER_LOW(g_uBasePort), 0); outp(ATA_REG_CYLINDER_HIGH(g_uBasePort), 0); outp(ATA_REG_HEAD(g_uBasePort), g_bDevice | cHeads); bSts = AtaSubmitCommandAndWait(ATA_CMD_INIT_DEVICE_PARAMS); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Device %#x parameter initialization", bDevice); if (bSts & ATA_STS_DRQ) return AtaError(bSts, "DRQ is set after device %#x parameter initialization", bDevice); return 0; } int AtaReset(void) { uint8_t bSts; /* Set the reset flat. */ outp(ATA_REG_CONTROL(g_uBasePort), ATA_CTL_SRST); /** @todo This still needs work - it doesn't work when ERR is set. */ /* Wait for the busy flag response. */ for (bSts = 0; bSts < 20; bSts++) ATA_DELAY_400NS(); while (!(bSts = inp(ATA_REG_STATUS(g_uBasePort))) & ATA_STS_BUSY) ATA_DELAY_400NS(); /* Clear the reset flag. */ outp(ATA_REG_CONTROL(g_uBasePort), 0); ATA_DELAY_400NS(); /* Wait for the controller to become non-busy. */ bSts = AtaWaitBusy(); if (bSts & ATA_STS_ERR) return AtaError(bSts, "Software reset failed"); return 0; } int AtaReInit(void) { int rc; /* Reset the controller + devices. */ if (AtaReset() != 0) return -1; if (g_fSupportsSetFeature8BitData) { if (g_f8BitData) rc = AtaSetFeature(g_bDevice, ATA_FEATURE_EN_8BIT_DATA, 0); else rc = AtaSetFeature(g_bDevice, ATA_FEATURE_DI_8BIT_DATA, 0); if (rc != 0) return rc; } /* Try disable write cache. */ if (g_fSupportsSetFeatureWriteCache) { rc = AtaSetFeature(g_bDevice, ATA_FEATURE_DI_WRITE_CACHE, 0); if (rc != 0) return rc; } /* Select PIO mode without IORDY. */ if (g_fSupportsSetFeatureXferMode) { rc = AtaSetFeature(g_bDevice, ATA_FEATURE_SET_XFERMODE, ATA_FV_XFERMODE_PIO_MODE_DEFAULT_NO_IORDY); if (rc != 0) return rc; } return 0; } int AtaInit(uint16_t uBasePort, uint16_t uCtrlPort, uint8_t cPortShift, uint8_t bDevice, uint8_t f8BitData) { int rc; uint8_t bSts, bStsAlt; g_uBasePort = uBasePort; g_uCtrlPort = uCtrlPort; g_cPortShift = cPortShift; g_bDevice = bDevice; g_f8BitData = f8BitData; g_fSupportsSetFeature8BitData = 1; g_fSupportsSetFeatureWriteCache = 1; g_fSupportsSetFeatureXferMode = 1; /* Check whether the two status registers match up. If they don't we probably don't have a controller at this address. */ inp(ATA_REG_STATUS(g_uBasePort)); bSts = inp(ATA_REG_STATUS(g_uBasePort)); bStsAlt = inp(ATA_REG_ALT_STATUS(g_uCtrlPort)); if (bSts != bStsAlt || bSts == 0xff) { fprintf(stderr, "Status register differs or is 0xff\n"); fprintf(stderr, " port %#05x status=", ATA_REG_STATUS(g_uBasePort)); AtaPrintStatus(stderr, bSts); fprintf(stderr, "\n"); fprintf(stderr, " port %#05x alt status=", ATA_REG_ALT_STATUS(g_uCtrlPort)); AtaPrintStatus(stderr, bStsAlt); fprintf(stderr, "\n"); return -1; } printf("Pre init status="); AtaPrintStatus(stdout, bSts); printf("\n"); for (;;) { /* Reset the controller + devices. */ if (AtaReset() != 0) return -1; /* Enable 8-bit data transfers (just to be on the safe side). */ if (g_fSupportsSetFeature8BitData) { if (g_f8BitData) rc = AtaSetFeature(g_bDevice, ATA_FEATURE_EN_8BIT_DATA, 0); else rc = AtaSetFeature(g_bDevice, ATA_FEATURE_DI_8BIT_DATA, 0); if (rc != 0) { fprintf(stderr, f8BitData ? "warning: ATA_FEATURE_EN_8BIT_DATA failed, assuming not supported. Retrying in 16-bit mode." : "warning: ATA_FEATURE_DI_8BIT_DATA failed, assuming not supported. Retrying."); g_fSupportsSetFeature8BitData = 0; g_f8BitData = 0; continue; } } /* Try disable write cache. */ if (g_fSupportsSetFeatureWriteCache) { rc = AtaSetFeature(g_bDevice, ATA_FEATURE_DI_WRITE_CACHE, 0); if (rc != 0) { fprintf(stderr, "warning: ATA_FEATURE_DI_WRITE_CACHE failed, assuming not supported. Retrying."); g_fSupportsSetFeatureWriteCache = 0; continue; } } /* Select PIO mode without IORDY. */ if (g_fSupportsSetFeatureXferMode) { rc = AtaSetFeature(g_bDevice, ATA_FEATURE_SET_XFERMODE, ATA_FV_XFERMODE_PIO_MODE_DEFAULT_NO_IORDY); if (rc != 0) { fprintf(stderr, "warning: ATA_FEATURE_SET_XFERMODE = DEFAULT_NO_IORDY failed, assuming not supported. Retrying."); g_fSupportsSetFeatureXferMode = 0; continue; } } /* Identify the device. */ memset(g_awIdentify, 0, sizeof(g_awIdentify)); if (AtaIdentifyDevice(g_bDevice, g_awIdentify) != 0) return -1; /** @todo this is rather simple... */ g_cCylinders = g_awIdentify[1]; g_cHeads = g_awIdentify[3]; g_cSectorsPerTrack = g_awIdentify[6]; g_cSectorsPerCylinder = g_cHeads * g_cSectorsPerTrack; printf("Device %#x at %#x/%#x: %u cylinders, %u heads, %u sectors, %s data\n", g_bDevice, g_uBasePort, g_uCtrlPort, g_cCylinders, g_cHeads, g_cSectorsPerTrack, g_f8BitData ? "8-bit" : "16-bit"); /* Figure 5-9 in SanDisk Manual Rev 12.0: */ printf("debug: word82=%#x word83=%#x word84=%#x\n", g_awIdentify[82], g_awIdentify[83], g_awIdentify[84]); if ( g_awIdentify[82] != 0 && g_awIdentify[82] != UINT16_C(0xffff) && g_awIdentify[83] != 0 && g_awIdentify[83] != UINT16_C(0xffff) && g_awIdentify[84] != 0 && g_awIdentify[84] != UINT16_C(0xffff) && (g_awIdentify[83] & UINT16_C(0xc000)) == UINT16_C(0x4000) && (g_awIdentify[84] & UINT16_C(0xc000)) == UINT16_C(0x4000) ) { g_fSupportsWriteBuffer = (g_awIdentify[82] & UINT16_C(0x1000)) != 0; g_fSupportsReadBuffer = (g_awIdentify[82] & UINT16_C(0x2000)) != 0; } printf(" %s WRITE_BUFFER, %s READ_BUFFER\n", g_fSupportsWriteBuffer == 1 ? "have" : g_fSupportsWriteBuffer == 0 ? "no" : "uncertain", g_fSupportsReadBuffer == 1 ? "have" : g_fSupportsReadBuffer == 0 ? "no" : "uncertain"); break; } return 0; } int AtaArgMatchWithValue(const char *pszArg, const char *pszMatch, size_t cchMatch, int cArgs, char **papszArgs, int *piArg, const char **ppszValue) { if (strncmp(pszArg, pszMatch, cchMatch) == 0) { pszArg += cchMatch; if (!*pszArg) { if (*piArg < cArgs) { *ppszValue = papszArgs[*piArg]; *piArg += 1; } else { fprintf(stderr, "syntax error: Option '%s' is missing argument value\n", pszMatch); *ppszValue = NULL; } return 1; } if (*pszArg == ':' || *pszArg == '=') { if (!*pszArg) fprintf(stderr, "syntax error: Option '%s' is missing argument value\n", pszMatch); *ppszValue = pszArg; return 1; } } return 0; } int AtaInitFromArgv(int iArg, int cArgs, char **papszArgs) { uint8_t bDevice = ATA_DEV_MASTER; #if 0 uint16_t uBasePort = 0x1f0; /* Primary ATA host controller. */ uint16_t uCtrlPort = 0x3f0; /* The control block of the primary ATA host controller. */ uint8_t cShiftPort = 0; uint8_t f8BitData = 0; #else uint16_t uBasePort = 0x300; /* Lo-tech CF-lite. */ uint16_t uCtrlPort = 0x310; /* Lo-tech CF-lite. */ uint8_t cShiftPort = 1; /* Special Lo-tech CF-lite hack. */ uint8_t f8BitData = 1; #endif while (iArg < cArgs) { const char *pszArg = papszArgs[iArg++]; const char *pszValue; int iWhich = 0; if ( (iWhich = AtaArgMatchWithValue(pszArg, STR_TUPLE("--base-port"), cArgs, papszArgs, &iArg, &pszValue)) || (iWhich = AtaArgMatchWithValue(pszArg, STR_TUPLE("-b"), cArgs, papszArgs, &iArg, &pszValue)) || (iWhich = AtaArgMatchWithValue(pszArg, STR_TUPLE("--ctrl-port"), cArgs, papszArgs, &iArg, &pszValue) * 2) || (iWhich = AtaArgMatchWithValue(pszArg, STR_TUPLE("-c"), cArgs, papszArgs, &iArg, &pszValue) * 2) ) { unsigned long uTmp = strtoul(pszValue, NULL, 16); if (uTmp < 16 || uTmp >= 1024) { fprintf(stderr, "error: Invalid port number %#lx for %s (valid range 0x010..0x3ff)\n", uTmp, pszArg); return -1; } if (iWhich == 1) uBasePort = (uint16_t)uTmp; else uCtrlPort = (uint16_t)uTmp; } else if ( AtaArgMatchWithValue(pszArg, STR_TUPLE("--port-shift"), cArgs, papszArgs, &iArg, &pszValue) || AtaArgMatchWithValue(pszArg, STR_TUPLE("-s"), cArgs, papszArgs, &iArg, &pszValue) ) { unsigned long uTmp = strtoul(pszValue, NULL, 0); if (uTmp >= 4) { fprintf(stderr, "error: Invalid port shift number %#lx (valid range 0..3)\n", uTmp); return -1; } cShiftPort = (uint8_t)uTmp; } else if ( AtaArgMatchWithValue(pszArg, STR_TUPLE("--device"), cArgs, papszArgs, &iArg, &pszValue) || AtaArgMatchWithValue(pszArg, STR_TUPLE("-d"), cArgs, papszArgs, &iArg, &pszValue) ) { unsigned long uTmp = strtoul(pszValue, NULL, 16); if ( uTmp != ATA_DEV_MASTER && uTmp != ATA_DEV_SLAVE) { fprintf(stderr, "error: Invalid device number %#lx; only %#x (master) or %#x (slave) are allowed.\n", uTmp, ATA_DEV_MASTER, ATA_DEV_SLAVE); return -1; } bDevice = (uint8_t)uTmp; } else if ( strcmp(pszArg, "--8-bit-data") == 0 || strcmp(pszArg, "-8") == 0) f8BitData = 1; else if ( strcmp(pszArg, "--8-bit-data-plus") == 0 || strcmp(pszArg, "-8+") == 0) f8BitData = 3; else if ( strcmp(pszArg, "--16-bit-data") == 0 || strcmp(pszArg, "-16") == 0) f8BitData = 0; else if (strcmp(pszArg, "--ide") == 0) { /* Normal IDE, primary. */ uBasePort = 0x1f0; uCtrlPort = 0x3f0; cShiftPort = 0; f8BitData = 0; } else if (strcmp(pszArg, "--xt-cf") == 0) { /* Lo-tech CF-lite. */ uBasePort = 0x300; uCtrlPort = 0x310; cShiftPort = 1; f8BitData = 1; } } return AtaInit(uBasePort, uCtrlPort, cShiftPort, bDevice, f8BitData); }