Index: branches/prasys/i386/libsaio/Makefile =================================================================== --- branches/prasys/i386/libsaio/Makefile (revision 50) +++ branches/prasys/i386/libsaio/Makefile (revision 51) @@ -41,7 +41,7 @@ smbios_patcher.o fake_efi.o ext2fs.o \ hpet.o spd.o usb.o pci_setup.o \ device_inject.o nvidia.o ati.o gma.o 915resolution.o edid.o \ - mindrvr.o cddrvr.o \ + mindrvr.o cddrvr.o \ SAIO_EXTERN_OBJS = console.o Index: branches/prasys/i386/libsaio/cddrvr.c =================================================================== --- branches/prasys/i386/libsaio/cddrvr.c (revision 0) +++ branches/prasys/i386/libsaio/cddrvr.c (revision 51) @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2009 netkas. All rights reserved. + * Redistribution and use in binary form for direct or indirect commercial purposes, with or without + * modification, is stricktly forbidden. + * Redistributions in binary form for non-commercial purposes must reproduce the above license notice, + * this list of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * Neither the names of EFI V1-V10 copyright owner nor the names of its contributors may be used + * to endorse or promote products derived direct or indirect from this software. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "libsaio.h" +#include "mindrvr.h" + +#define CDB_SIZE_T 12 +uint8_t cdb[CDB_SIZE_T]; + +uint32_t cd_drives[32][4]; +uint32_t cd_count = 0; + +uint8_t cd_bad[10] = {0,0,0,0,0,0,0,0,0,0}; + +uint32_t pci_count = 0; +uint32_t pci_devs[ 768 ]; +uint32_t curdrive = 0; +#define CACHE_BLKN 4 +unsigned char * cache_2k; +unsigned int cache_blk; + +static void DriveReset(uint32_t dev) +{ + if (dev > cd_count) + { + printf("device %d doesnt exist, max is %d\n", dev, cd_count); + return; + } + reg_reset(cd_drives[dev][2]); +} + +static int SelectDrive(uint32_t dev) +{ + if(cd_bad[dev] == 1) return 1; + if(curdrive == dev) return 0; + if (dev > cd_count) + { + printf("device %d doesnt exist, max is %d\n", dev, cd_count); + return 1; + } + pio_set_iobase_addr(cd_drives[dev][0], cd_drives[dev][1], cd_drives[dev][3]); +// reg_config(); + reg_config_info[0]=0; + reg_config_info[1]=0; + reg_config_info[cd_drives[dev][2]]=REG_CONFIG_TYPE_ATAPI; + curdrive = dev; +// DriveReset(dev); + return 0; +} + +static int cdcheck (int dev, char * buffer) +{ +uint32_t result, try, wait_time; + +if(SelectDrive(dev)) +{ + printf("Select drive returned 1, thats bad\n"); + return -1; +}; + + +for (try=0; try<10; ++try) + { + memset( cdb, 0, sizeof( cdb ) ); + cdb[0] = 0xa8; + cdb[1] = 0x00; + cdb[2] = 0x00; + cdb[3] = 0x00; + cdb[4] = 0x00; + cdb[5] = 0x00; + cdb[6] = 0x00; + cdb[7] = 0x00; + cdb[8] = 0x00; + cdb[9] = 0x01; + cdb[10] = 0x00; + cdb[11] = 0x00; + result = reg_packet(cd_drives[dev][2], sizeof(cdb), (unsigned char*) cdb, 0, 2048, cache_2k); + if(!result) + { + return 0; + } + wait_time = time18() + 20; + while(time18() < wait_time); + } +return -1; +} + + +static void AddDrive(uint32_t base1, uint32_t base2, uint32_t bmbase) +{ + uint32_t res,i; + pio_set_iobase_addr(base1, base2, bmbase); + res=reg_config(); + if(res>0) + { + printf( "AddDrive: Found %d devices, base1 is %x, base2 is %x, dev 0 is %d, dev 1 is %d.\n", + res, base1, base2, + reg_config_info[0] , + reg_config_info[1] ); + if(reg_config_info[0] == REG_CONFIG_TYPE_ATAPI) + { + i=++cd_count; + cd_drives[i][0] = base1; + cd_drives[i][1] = base2; + cd_drives[i][2] = 0; //master + cd_drives[i][3] = bmbase; + printf("added master device %d\n", i); + reg_reset(0); + } + if(reg_config_info[1] == REG_CONFIG_TYPE_ATAPI) + { + i=++cd_count; + cd_drives[i][0] = base1; + cd_drives[i][1] = base2; + cd_drives[i][2] = 1; //slave + cd_drives[i][3] = bmbase; + printf("added slave device %d\n", i); + reg_reset(1); + } + } + +} + +int setup_cdread(void) +{ + int res; + int trylegacy = 0; + uint16_t i; + unsigned int pcidev=0; + unsigned int index=0; + unsigned int vendid=0; + unsigned int devid=0; + unsigned int cmdBase=0; + unsigned int ctrlBase=0; + unsigned int classid=0; + unsigned int bmideBase=0; + if(cd_count>0) return cd_count; + + for(i=0; i<0x05ff;i++) + { + vendid = GetPciDword( i, 0x00) & 0xffff; + devid = (GetPciDword( i, 0x00) >>16) & 0xffff; + if((vendid != 0xffff) && (vendid != 0x0000) && (devid != 0x2825) && (devid != 0x2921) && (devid != 0x2926) && (devid != 0x3a06) && (devid != 0x3a26))// && (vendid != 0x1283) && (vendid != 0x197b)) // dont need jmicron and ite + { +// devid = (GetPciDword( i, 0x00) >>16) & 0xffff; + classid = (GetPciDword( i, 0x08) >>16) & 0xffff; + if(classid == 0x0101) + { + printf("device %x, vendid %x, devid %x, classid %x\n", i, vendid, devid, classid); + cmdBase = GetPciDword(i, 0x10) & 0xfffe; + ctrlBase = GetPciDword(i, 0x14) & 0xfffe; + bmideBase = GetPciDword(i, 0x20) & 0xffff; + if(cmdBase && ctrlBase)AddDrive(cmdBase, ctrlBase, bmideBase); //no need in bmidebase? + if(cmdBase == 0 && ctrlBase == 0) trylegacy=1; + printf("pri port cmdbase - %x, ctrlbase - %x, bmideBase - %x\n", cmdBase, ctrlBase, bmideBase); + cmdBase = GetPciDword(i, 0x10+8) & 0xfffe; + ctrlBase = GetPciDword(i, 0x14+8) & 0xfffe; + if(cmdBase && ctrlBase)AddDrive(cmdBase, ctrlBase, bmideBase); + printf("sec port cmdbase - %x, ctrlbase - %x, bmideBase - %x\n", cmdBase, ctrlBase, bmideBase); + } + } + } + if(trylegacy) + { + AddDrive(0x1f0, 0x3f4, 0x00); + AddDrive(0x170, 0x374, 0x00); + } + if(cd_count == 0) return 0; +// AddDrive(0xa000, 0x9c00, 0x00); //ich9r pri +// AddDrive(0x9880, 0x9800, 0x00); //ich9r sec +// AddDrive(0xd600, 0xd700, 0x00); +// AddDrive(0xd800, 0xd900, 0x00); + cache_2k = malloc(2048*CACHE_BLKN); + cache_blk = 0xFFFF0000; + for(i=1; i<=cd_count;i++) + if(cdcheck(i, cache_2k)) + { + printf("no inserted cd found in drive %d\n",i); + cd_bad[i]=1; + } + printf("cd setup done\n"); + return cd_count; +} + +int cdread (int dev, unsigned int secno, char * buffer) +{ +uint32_t result, try, wait_time, olddrive; + +olddrive=curdrive; //saving curdrive until its overwrited in SelectDrive + +if(SelectDrive(dev)) +{ + printf("Select drive returned 1, thats bad\n"); + return -1; +}; + +if(secno >= cache_blk && secno < (cache_blk + CACHE_BLKN) && olddrive == dev) +{ + bcopy(cache_2k + (secno - cache_blk) * 2048, buffer, 2048); + return 0; +} +for (try=0; try<10; ++try) + { + memset( cdb, 0, sizeof( cdb ) ); + cdb[0] = 0xa8; + cdb[1] = 0x00; + cdb[2] = (OSSwapBigToHostInt32(secno) & 0xFF); //4 + cdb[3] = (OSSwapBigToHostInt32(secno) & 0xFF00) >> 8; //3 + cdb[4] = (OSSwapBigToHostInt32(secno) & 0xFF0000) >> 16; //2 + cdb[5] = (OSSwapBigToHostInt32(secno) & 0xFF000000) >> 24; //1 + cdb[6] = 0x00; + cdb[7] = 0x00; + cdb[8] = 0x00; + cdb[9] = CACHE_BLKN; + cdb[10] = 0x00; + cdb[11] = 0x00; + + result = reg_packet(cd_drives[dev][2], sizeof(cdb), (unsigned char*) cdb, 0, 2048*CACHE_BLKN, cache_2k); + if(!result) + { + bcopy(cache_2k, buffer, 2048); + cache_blk=secno; + return 0; + } + // DriveReset(dev); + wait_time = time18() + 50; + while(time18() < wait_time); + } +return -1; +} + +void ejectcd(uint32_t dev, uint32_t dir) +{ + SelectDrive(dev); + memset( cdb, 0, sizeof( cdb ) ); + + cdb[0] = 0x1b; + cdb[1] = 0x00; + cdb[2] = 0x00; + cdb[3] = 0x00; + cdb[4] = dir ? 2 :3; + cdb[5] = 0x00; + cdb[6] = 0x00; + cdb[7] = 0x00; + cdb[8] = 0x00; + cdb[9] = 0x00; + cdb[10] = 0x00; + cdb[11] = 0x00; + + reg_packet(cd_drives[dev][2], sizeof(cdb), (unsigned char*) cdb, 0, 2048, kLoadAddr); +// DriveReset(dev); +} \ No newline at end of file Property changes on: branches/prasys/i386/libsaio/cddrvr.c ___________________________________________________________________ Added: svn:executable + * Index: branches/prasys/i386/libsaio/915resolution.c =================================================================== --- branches/prasys/i386/libsaio/915resolution.c (revision 0) +++ branches/prasys/i386/libsaio/915resolution.c (revision 51) @@ -0,0 +1,733 @@ + +/* Copied from 915 resolution created by steve tomljenovic + * + * This code is based on the techniques used in : + * + * - 855patch. Many thanks to Christian Zietz (czietz gmx net) + * for demonstrating how to shadow the VBIOS into system RAM + * and then modify it. + * + * - 1280patch by Andrew Tipton (andrewtipton null li). + * + * - 855resolution by Alain Poirier + * + * This source code is into the public domain. + */ + +#include "libsaio.h" +#include "915resolution.h" + +char * chipset_type_names[] = { + "UNKNOWN", "830", "845G", "855GM", "865G", "915G", "915GM", "945G", "945GM", "945GME", + "946GZ", "G965", "Q965", "965GM", "500" +}; + +char * bios_type_names[] = {"UNKNOWN", "TYPE 1", "TYPE 2", "TYPE 3"}; + +int freqs[] = { 60, 75, 85 }; + +UInt32 get_chipset_id(void) { + outl(0xcf8, 0x80000000); + return inl(0xcfc); +} + +chipset_type get_chipset(UInt32 id) { + chipset_type type; + + switch (id) { + case 0x35758086: + type = CT_830; + break; + + case 0x25608086: + type = CT_845G; + break; + + case 0x35808086: + type = CT_855GM; + break; + + case 0x25708086: + type = CT_865G; + break; + + case 0x25808086: + type = CT_915G; + break; + + case 0x25908086: + type = CT_915GM; + break; + + case 0x27708086: + type = CT_945G; + break; + + case 0x27a08086: + type = CT_945GM; + break; + + case 0x27ac8086: + type = CT_945GME; + break; + + case 0x29708086: + type = CT_946GZ; + break; + + case 0x29a08086: + type = CT_G965; + break; + + case 0x29908086: + type = CT_Q965; + break; + + case 0x81008086: + type = CT_500; + break; + + case 0x2a008086: + type = CT_965GM; + break; + + + default: + type = CT_UNKWN; + break; + } + return type; +} + +vbios_resolution_type1 * map_type1_resolution(vbios_map * map, UInt16 res) { + vbios_resolution_type1 * ptr = ((vbios_resolution_type1*)(map->bios_ptr + res)); + return ptr; +} + +vbios_resolution_type2 * map_type2_resolution(vbios_map * map, UInt16 res) { + vbios_resolution_type2 * ptr = ((vbios_resolution_type2*)(map->bios_ptr + res)); + return ptr; +} + +vbios_resolution_type3 * map_type3_resolution(vbios_map * map, UInt16 res) { + vbios_resolution_type3 * ptr = ((vbios_resolution_type3*)(map->bios_ptr + res)); + return ptr; +} + +char detect_bios_type(vbios_map * map, char modeline, int entry_size) { + UInt32 i; + UInt16 r1, r2; + + r1 = r2 = 32000; + + for (i=0; i < map->mode_table_size; i++) { + if (map->mode_table[i].resolution <= r1) { + r1 = map->mode_table[i].resolution; + } + else { + if (map->mode_table[i].resolution <= r2) { + r2 = map->mode_table[i].resolution; + } + } + + /*printf("r1 = %d r2 = %d\n", r1, r2);*/ + } + + return (r2-r1-6) % entry_size == 0; +} + +void close_vbios(vbios_map * map); + +vbios_map * open_vbios(chipset_type forced_chipset) { + UInt32 z; + vbios_map * map = NEW(vbios_map); + for(z=0; zchipset_id = get_chipset_id(); + map->chipset = get_chipset(map->chipset_id); + } + else if (forced_chipset != CT_UNKWN) { + map->chipset = forced_chipset; + } + else { + map->chipset = CT_915GM; + } + + /* + * Map the video bios to memory + */ + + map->bios_ptr=(char*)VBIOS_START; + + /* + * check if we have ATI Radeon + */ + + /*if (memmem(map->bios_ptr, VBIOS_SIZE, ATI_SIGNATURE1, strlen(ATI_SIGNATURE1)) || + memmem(map->bios_ptr, VBIOS_SIZE, ATI_SIGNATURE2, strlen(ATI_SIGNATURE2)) ) { + printf(stderr, "ATI chipset detected. 915resolution only works with Intel 800/900 series graphic chipsets.\n"); + return 0; + }*/ + + /* + * check if we have NVIDIA + */ + + /*if (memmem(map->bios_ptr, VBIOS_SIZE, NVIDIA_SIGNATURE, strlen(NVIDIA_SIGNATURE))) { + printf("NVIDIA chipset detected. 915resolution only works with Intel 800/900 series graphic chipsets.\n"); + return 0; + }*/ + + /* + * check if we have Intel + */ + + /*if (map->chipset == CT_UNKWN && memmem(map->bios_ptr, VBIOS_SIZE, INTEL_SIGNATURE, strlen(INTEL_SIGNATURE))) { + printf( "Intel chipset detected. However, 915resolution was unable to determine the chipset type.\n"); + + printf("Chipset Id: %x\n", map->chipset_id); + + printf("Please report this problem to stomljen@yahoo.com\n"); + + close_vbios(map); + return 0; + }*/ + + /* + * check for others + */ + + if (map->chipset == CT_UNKWN) { + printf("Unknown chipset type and unrecognized bios.\n"); + + printf("915resolution only works with Intel 800/900 series graphic chipsets.\n"); + + printf("Chipset Id: %x\n", map->chipset_id); + close_vbios(map); + return 0; + } + + /* + * Figure out where the mode table is + */ + + { + char* p = map->bios_ptr + 16; + char* limit = map->bios_ptr + VBIOS_SIZE - (3 * sizeof(vbios_mode)); + + while (p < limit && map->mode_table == 0) { + vbios_mode * mode_ptr = (vbios_mode *) p; + + if (((mode_ptr[0].mode & 0xf0) == 0x30) && ((mode_ptr[1].mode & 0xf0) == 0x30) && + ((mode_ptr[2].mode & 0xf0) == 0x30) && ((mode_ptr[3].mode & 0xf0) == 0x30)) { + + map->mode_table = mode_ptr; + } + + p++; + } + + if (map->mode_table == 0) { + printf("Unable to locate the mode table.\n"); + printf("Please run the program 'dump_bios' as root and\n"); + printf("email the file 'vbios.dmp' to stomljen@yahoo.com.\n"); + printf("Chipset: %s\n", chipset_type_names[map->chipset]); + close_vbios(map); + return 0; + } + } + + /* + * Determine size of mode table + */ + + { + vbios_mode * mode_ptr = map->mode_table; + + while (mode_ptr->mode != 0xff) { + map->mode_table_size++; + mode_ptr++; + } + } + + /* + * Figure out what type of bios we have + * order of detection is important + */ + + if (detect_bios_type(map, TRUE, sizeof(vbios_modeline_type3))) { + map->bios = BT_3; + } + else if (detect_bios_type(map, TRUE, sizeof(vbios_modeline_type2))) { + map->bios = BT_2; + } + else if (detect_bios_type(map, FALSE, sizeof(vbios_resolution_type1))) { + map->bios = BT_1; + } + else { + printf("Unable to determine bios type.\n"); + printf("Please run the program 'dump_bios' as root and\n"); + printf("email the file 'vbios.dmp' to stomljen@yahoo.com.\n"); + + printf("Chipset: %s\n", chipset_type_names[map->chipset]); + printf("Mode Table Offset: $C0000 + $%x\n", ((UInt32)map->mode_table) - ((UInt32)map->bios_ptr)); + + printf("Mode Table Entries: %u\n", map->mode_table_size); + return 0; + } + + return map; +} + +void close_vbios(vbios_map * map) { + FREE(map); +} + +void unlock_vbios(vbios_map * map) { + + map->unlocked = TRUE; + + switch (map->chipset) { + case CT_UNKWN: + break; + case CT_830: + case CT_855GM: + outl(0xcf8, 0x8000005a); + map->b1 = inb(0xcfe); + + outl(0xcf8, 0x8000005a); + outb(0xcfe, 0x33); + break; + case CT_845G: + case CT_865G: + case CT_915G: + case CT_915GM: + case CT_945G: + case CT_945GM: + case CT_945GME: + case CT_946GZ: + case CT_G965: + case CT_Q965: + case CT_965GM: + case CT_500: + + outl(0xcf8, 0x80000090); + map->b1 = inb(0xcfd); + map->b2 = inb(0xcfe); + outl(0xcf8, 0x80000090); + outb(0xcfd, 0x33); + outb(0xcfe, 0x33); + break; + } + + #if DEBUG + { + UInt32 t = inl(0xcfc); + printf("unlock PAM: (0x%08x)\n", t); + } +#endif +} + +void relock_vbios(vbios_map * map) { + + map->unlocked = FALSE; + + switch (map->chipset) { + case CT_UNKWN: + break; + case CT_830: + case CT_855GM: + outl(0xcf8, 0x8000005a); + outb(0xcfe, map->b1); + break; + case CT_845G: + case CT_865G: + case CT_915G: + case CT_915GM: + case CT_945G: + case CT_945GM: + case CT_945GME: + case CT_946GZ: + case CT_G965: + case CT_Q965: + case CT_965GM: + case CT_500: + + outl(0xcf8, 0x80000090); + outb(0xcfd, map->b1); + outb(0xcfe, map->b2); + break; + } + + #if DEBUG + { + UInt32 t = inl(0xcfc); + printf("relock PAM: (0x%08x)\n", t); + } + #endif +} + + +static void gtf_timings(UInt32 x, UInt32 y, UInt32 freq, + unsigned long *clock, + UInt16 *hsyncstart, UInt16 *hsyncend, UInt16 *hblank, + UInt16 *vsyncstart, UInt16 *vsyncend, UInt16 *vblank) +{ + UInt32 hbl, vbl, vfreq; + + vbl = y + (y+1)/(20000.0/(11*freq) - 1) + 1.5; + vfreq = vbl * freq; + hbl = 16 * (int)(x * (30.0 - 300000.0 / vfreq) / + + (70.0 + 300000.0 / vfreq) / 16.0 + 0.5); + + *vsyncstart = y; + *vsyncend = y + 3; + *vblank = vbl - 1; + *hsyncstart = x + hbl / 2 - (x + hbl + 50) / 100 * 8 - 1; + *hsyncend = x + hbl / 2 - 1; + *hblank = x + hbl - 1; + *clock = (x + hbl) * vfreq / 1000; +} + +void set_mode(vbios_map * map, /*UInt32 mode,*/ UInt32 x, UInt32 y, UInt32 bp, UInt32 htotal, UInt32 vtotal) { + UInt32 xprev, yprev; + UInt32 i = 0, j; // patch first available mode + +// for (i=0; i < map->mode_table_size; i++) { +// if (map->mode_table[0].mode == mode) { + switch(map->bios) { + case BT_1: + { + vbios_resolution_type1 * res = map_type1_resolution(map, map->mode_table[i].resolution); + + if (bp) { + map->mode_table[i].bits_per_pixel = bp; + } + + res->x2 = (htotal?(((htotal-x) >> 8) & 0x0f) : (res->x2 & 0x0f)) | ((x >> 4) & 0xf0); + res->x1 = (x & 0xff); + + res->y2 = (vtotal?(((vtotal-y) >> 8) & 0x0f) : (res->y2 & 0x0f)) | ((y >> 4) & 0xf0); + res->y1 = (y & 0xff); + if (htotal) + res->x_total = ((htotal-x) & 0xff); + + if (vtotal) + res->y_total = ((vtotal-y) & 0xff); + } + break; + case BT_2: + { + vbios_resolution_type2 * res = map_type2_resolution(map, map->mode_table[i].resolution); + + res->xchars = x / 8; + res->ychars = y / 16 - 1; + xprev = res->modelines[0].x1; + yprev = res->modelines[0].y1; + + for(j=0; j < 3; j++) { + vbios_modeline_type2 * modeline = &res->modelines[j]; + + if (modeline->x1 == xprev && modeline->y1 == yprev) { + modeline->x1 = modeline->x2 = x-1; + modeline->y1 = modeline->y2 = y-1; + + gtf_timings(x, y, freqs[j], &modeline->clock, + &modeline->hsyncstart, &modeline->hsyncend, + &modeline->hblank, &modeline->vsyncstart, + &modeline->vsyncend, &modeline->vblank); + + if (htotal) + modeline->htotal = htotal; + else + modeline->htotal = modeline->hblank; + + if (vtotal) + modeline->vtotal = vtotal; + else + modeline->vtotal = modeline->vblank; + } + } + } + break; + case BT_3: + { + vbios_resolution_type3 * res = map_type3_resolution(map, map->mode_table[i].resolution); + + xprev = res->modelines[0].x1; + yprev = res->modelines[0].y1; + + for (j=0; j < 3; j++) { + vbios_modeline_type3 * modeline = &res->modelines[j]; + + if (modeline->x1 == xprev && modeline->y1 == yprev) { + modeline->x1 = modeline->x2 = x-1; + modeline->y1 = modeline->y2 = y-1; + + gtf_timings(x, y, freqs[j], &modeline->clock, + &modeline->hsyncstart, &modeline->hsyncend, + &modeline->hblank, &modeline->vsyncstart, + &modeline->vsyncend, &modeline->vblank); + if (htotal) + modeline->htotal = htotal; + else + modeline->htotal = modeline->hblank; + if (vtotal) + modeline->vtotal = vtotal; + else + modeline->vtotal = modeline->vblank; + + modeline->timing_h = y-1; + modeline->timing_v = x-1; + } + } + } + break; + case BT_UNKWN: + break; + } +// } +// } +} + +void display_map_info(vbios_map * map) { + printf("Chipset: %s\n", chipset_type_names[map->chipset]); + printf("BIOS: %s\n", bios_type_names[map->bios]); + + printf("Mode Table Offset: $C0000 + $%x\n", ((UInt32)map->mode_table) - ((UInt32)map->bios_ptr)); + printf("Mode Table Entries: %u\n", map->mode_table_size); +} + +/* +int parse_args(int argc, char *argv[], chipset_type *forced_chipset, UInt32 *list, UInt32 *mode, UInt32 *x, UInt32 *y, UInt32 *bp, UInt32 *raw, UInt32 *htotal, UInt32 *vtotal) { + UInt32 index = 0; + + *list = *mode = *x = *y = *raw = *htotal = *vtotal = 0; + + *forced_chipset = CT_UNKWN; + + if ((argc > index) && !strcmp(argv[index], "-c")) { + index++; + + + + if(argc<=index) { + + return 0; + + } + + + + if (!strcmp(argv[index], "845")) { + + *forced_chipset = CT_845G; + + } + + else if (!strcmp(argv[index], "855")) { + + *forced_chipset = CT_855GM; + + } + + else if (!strcmp(argv[index], "865")) { + + *forced_chipset = CT_865G; + + } + + else if (!strcmp(argv[index], "915G")) { + + *forced_chipset = CT_915G; + + } + + else if (!strcmp(argv[index], "915GM")) { + + *forced_chipset = CT_915GM; + + } + + else if (!strcmp(argv[index], "945G")) { + + *forced_chipset = CT_945G; + + } + + else if (!strcmp(argv[index], "945GM")) { + + *forced_chipset = CT_945GM; + + } + + else if (!strcmp(argv[index], "945GME")) { + + *forced_chipset = CT_945GME; + + } + + else if (!strcmp(argv[index], "946GZ")) { + + *forced_chipset = CT_946GZ; + + } + + else if (!strcmp(argv[index], "G965")) { + + *forced_chipset = CT_G965; + + } + + else if (!strcmp(argv[index], "Q965")) { + + *forced_chipset = CT_Q965; + + } + + else if (!strcmp(argv[index], "500")) { + + *forced_chipset = CT_500; + + } + + + else { + + printf("No match for forced chipset: %s\n", argv[index]); + + *forced_chipset = CT_UNKWN; + + } + + + + index++; + + + + if (argc<=index) { + + return 0; + + } + + } + + + + if ((argc > index) && !strcmp(argv[index], "-l")) { + + *list = 1; + + index++; + + + + if(argc<=index) { + + return 0; + + } + + } + + + + if ((argc > index) && !strcmp(argv[index], "-r")) { + + *raw = 1; + + index++; + + + + if(argc<=index) { + + return 0; + + } + + } + + + + if (argc-index < 3 || argc-index > 6) { + + return -1; + + } + + + + *mode = (UInt32) strtoul(argv[index], NULL, 16); + + *x = (UInt32)strtoul(argv[index+1], NULL, 10); + + *y = (UInt32)strtoul(argv[index+2], NULL, 10); + + + + + + if (argc-index > 3) { + + *bp = (UInt32)strtoul(argv[index+3], NULL, 10); + + } + + else { + + *bp = 0; + + } + + + + if (argc-index > 4) { + + *htotal = (UInt32)strtoul(argv[index+4], NULL, 10); + + } + + else { + + *htotal = 0; + + } + + + + if (argc-index > 5) { + + *vtotal = (UInt32)strtoul(argv[index+5], NULL, 10); + + } + + else { + + *vtotal = 0; + + } + + + + return 0; + +} ++ + + + */ + + +void list_modes(vbios_map *map, UInt32 raw) { + UInt32 i, x, y; + + for (i=0; i < map->mode_table_size; i++) { + switch(map->bios) { + case BT_1: + { + vbios_resolution_type1 * res = map_type1_resolution(map, map->mode_table[i].resolution); + + x = ((((UInt32) res->x2) & 0xf0) << 4) | res->x1; + y = ((((UInt32) res->y2) & 0xf0) << 4) | res->y1; + + if (x != 0 && y != 0) { + printf("Mode %02x : %dx%d, %d bits/pixel\n", map->mode_table[i].mode, x, y, map->mode_table[i].bits_per_pixel); + } + + if (raw) + { + printf("Mode %02x (raw) :\n\t%02x %02x\n\t%02x\n\t%02x\n\t%02x\n\t%02x\n\t%02x\n\t%02x\n", map->mode_table[i].mode, res->unknow1[0],res->unknow1[1], res->x1,res->x_total,res->x2,res->y1,res->y_total,res->y2); + } + + } + break; + case BT_2: + { + vbios_resolution_type2 * res = map_type2_resolution(map, map->mode_table[i].resolution); + + x = res->modelines[0].x1+1; + y = res->modelines[0].y1+1; + + if (x != 0 && y != 0) { + printf("Mode %02x : %dx%d, %d bits/pixel\n", map->mode_table[i].mode, x, y, map->mode_table[i].bits_per_pixel); + } + } + break; + case BT_3: + { + vbios_resolution_type3 * res = map_type3_resolution(map, map->mode_table[i].resolution); + + x = res->modelines[0].x1+1; + y = res->modelines[0].y1+1; + + if (x != 0 && y != 0) { + printf("Mode %02x : %dx%d, %d bits/pixel\n", map->mode_table[i].mode, x, y, map->mode_table[i].bits_per_pixel); + } + } + break; + case BT_UNKWN: + break; + } + } +} + + +void usage() { + printf("Usage: 915resolution [-c chipset] [-l] [mode X Y] [bits/pixel] [htotal] [vtotal]\n"); + printf(" Set the resolution to XxY for a video mode\n"); + printf(" Bits per pixel are optional. htotal/vtotal settings are additionally optional.\n"); + printf(" Options:\n"); + printf(" -c force chipset type (THIS IS USED FOR DEBUG PURPOSES)\n"); + printf(" -l display the modes found in the video BIOS\n"); + printf(" -r display the modes found in the video BIOS in raw mode (THIS IS USED FOR DEBUG PURPOSES)\n"); +} + +/* +static err_t +cmd_915resolution (struct arg_list *state , + int argc , + char **argv ) +{ + vbios_map * map; + UInt32 list, mode, x, y, bp, raw, htotal, vtotal; + chipset_type forced_chipset; + + printf("Intel 500/800/900 Series VBIOS Hack : version %s\n\n", VERSION); + + if (parse_args(argc, argv, &forced_chipset, &list, &mode, &x, &y, &bp, &raw, &htotal, &vtotal) == -1) { + usage(); + return 2; + } + + map = open_vbios(forced_chipset); + display_map_info(map); + + printf("\n"); + + if (list) { + list_modes(map, raw); + } + + if (mode!=0 && x!=0 && y!=0) { + unlock_vbios(map); + + set_mode(map, mode, x, y, bp, htotal, vtotal); + + relock_vbios(map); + + printf("Patch mode %02x to resolution %dx%d complete\n", mode, x, y); + + if (list) { + list_modes(map, raw); + } + } + + close_vbios(map); + + return 0; +} +*/ Index: branches/prasys/i386/libsaio/edid.c =================================================================== --- branches/prasys/i386/libsaio/edid.c (revision 0) +++ branches/prasys/i386/libsaio/edid.c (revision 51) @@ -0,0 +1,138 @@ +/* + * edid.c + * + * + * Created by Evan Lojewski on 12/1/09. + * Copyright 2009. All rights reserved. + * + */ + + +#include "libsaio.h" +#include "edid.h" +#include "vbe.h" +//#include "graphics.h" + +static biosBuf_t bb; + +UInt32 xResolution = 0; +UInt32 yResolution = 0; +UInt32 bpResolution = 0; + +void getResolution(UInt32* x, UInt32* y, UInt32* bp) +{ + if(!xResolution && !yResolution && !bpResolution) + { + + char* edidInfo = readEDID(); + + if(!edidInfo) return; + + // TODO: check *all* resolutions reported and eithe ruse the highest, or the native resolution (if there is a flag for that) + xResolution = edidInfo[56] | ((edidInfo[58] & 0xF0) << 4); + yResolution = edidInfo[59] | ((edidInfo[61] & 0xF0) << 4); + + + //printf("H Active = %d", edidInfo[56] | ((edidInfo[58] & 0xF0) << 4) ); + //printf("V Active = %d", edidInfo[59] | ((edidInfo[61] & 0xF0) << 4) ); + + + bpResolution = 32; // assume 32bits + + + free( edidInfo ); + + if(!xResolution) xResolution = 1024; //DEFAULT_SCREEN_WIDTH; + if(!yResolution) yResolution = 768; //DEFAULT_SCREEN_HEIGTH; + + } + + *x = xResolution; + *y = yResolution; + *bp = bpResolution; + +} + +char* readEDID() +{ + SInt16 last_reported = -1; + UInt8 edidInfo[EDID_BLOCK_SIZE]; + + UInt8 pointer; + + UInt8 header1[] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}; + UInt8 header2[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + int status; + unsigned int blocks_left = 1; + + do + { + // TODO: This currently only retrieves the *last* block, make the block buffer expand as needed / calculated from the first block + + bzero( edidInfo, EDID_BLOCK_SIZE); + + status = getEDID(edidInfo, blocks_left); + + //printf("Buffer location: 0x%X\n", SEG(buffer) << 16 | OFF(buffer)); + + /* + int j, i; + for (j = 0; j < 8; j++) { + for(i = 0; i < 16; i++) printf("0x%X ", ebiosInfo[((i+1) * (j + 1)) - 1]); + + } + printf("\n"); + */ + + if(status == 0) + { + //if( edidInfo[0] == 0x00 || edidInfo[0] == 0xFF) + if((memcmp(edidInfo, header1, sizeof(header1)) != 0) || + (memcmp(edidInfo, header2, sizeof(header2)) != 0) ) + { + blocks_left--; + int reported = edidInfo[ EDID_V1_BLOCKS_TO_GO_OFFSET ]; + + if ( reported > blocks_left ) + { + + printf("EDID claims %d more blocks left\n", reported); + } + + if ( (last_reported <= reported && last_reported != -1) + || reported == 0xff + /* 0xff frequently comes up in corrupt edids */ + //|| reported == MAGIC + ) + { + printf("Last reported %d\n", last_reported); + printf( "EDID blocks left is wrong.\n" + "Your EDID is probably invalid.\n"); + return 0; + } + else + { + //printf("Reading EDID block\n"); + //printf("H Active = %d", ebiosInfo[56] | ((ebiosInfo[58] & 0xF0) << 4) ); + //printf("V Active = %d", ebiosInfo[59] | ((ebiosInfo[61] & 0xF0) << 4) ); + + last_reported = reported; + blocks_left = reported; + } + } + else + { + printf("Invalid block %d\n", blocks_left); + printf("Header1 = %d", memcmp(edidInfo, header1, sizeof(header1)) ); + printf("Header2 = %d", memcmp(edidInfo, header2, sizeof(header2)) ); + return 0; + } + } + blocks_left = 0; + } while(blocks_left); + + UInt8* ret = malloc(sizeof(edidInfo)); + memcpy(ret, edidInfo, sizeof(edidInfo)); + return ret; +} Index: branches/prasys/i386/libsaio/915resolution.h =================================================================== --- branches/prasys/i386/libsaio/915resolution.h (revision 0) +++ branches/prasys/i386/libsaio/915resolution.h (revision 51) @@ -0,0 +1,146 @@ + +/* Copied from 915 resolution created by steve tomljenovic + * + * This code is based on the techniques used in : + * + * - 855patch. Many thanks to Christian Zietz (czietz gmx net) + * for demonstrating how to shadow the VBIOS into system RAM + * and then modify it. + * + * - 1280patch by Andrew Tipton (andrewtipton null li). + * + * - 855resolution by Alain Poirier + * + * This source code is into the public domain. + */ +#ifndef __915_RESOLUTION_H +#define __915_RESOLUTION_H + +#define NEW(a) ((a *)(malloc(sizeof(a)))) +#define FREE(a) (free(a)) + +#define VBIOS_START 0xc0000 +#define VBIOS_SIZE 0x10000 + +#define FALSE 0 +#define TRUE 1 + +#define MODE_TABLE_OFFSET_845G 617 + + +#define ATI_SIGNATURE1 "ATI MOBILITY RADEON" +#define ATI_SIGNATURE2 "ATI Technologies Inc" +#define NVIDIA_SIGNATURE "NVIDIA Corp" +#define INTEL_SIGNATURE "Intel Corp" + + + + +typedef enum { + CT_UNKWN, CT_830, CT_845G, CT_855GM, CT_865G, CT_915G, CT_915GM, CT_945G, CT_945GM, CT_945GME, + CT_946GZ, CT_G965, CT_Q965, CT_965GM, CT_500 +} chipset_type; + + +typedef enum { + BT_UNKWN, BT_1, BT_2, BT_3 +} bios_type; + + +typedef struct { + UInt8 mode; + UInt8 bits_per_pixel; + UInt16 resolution; + UInt8 unknown; +} __attribute__((packed)) vbios_mode; + +typedef struct { + UInt8 unknow1[2]; + UInt8 x1; + UInt8 x_total; + UInt8 x2; + UInt8 y1; + UInt8 y_total; + UInt8 y2; +} __attribute__((packed)) vbios_resolution_type1; + +typedef struct { + unsigned long clock; + + UInt16 x1; + UInt16 htotal; + UInt16 x2; + UInt16 hblank; + UInt16 hsyncstart; + UInt16 hsyncend; + UInt16 y1; + UInt16 vtotal; + UInt16 y2; + UInt16 vblank; + UInt16 vsyncstart; + UInt16 vsyncend; +} __attribute__((packed)) vbios_modeline_type2; + +typedef struct { + UInt8 xchars; + UInt8 ychars; + UInt8 unknown[4]; + + vbios_modeline_type2 modelines[]; +} __attribute__((packed)) vbios_resolution_type2; + +typedef struct { + unsigned long clock; + + UInt16 x1; + UInt16 htotal; + UInt16 x2; + UInt16 hblank; + UInt16 hsyncstart; + UInt16 hsyncend; + + UInt16 y1; + UInt16 vtotal; + UInt16 y2; + UInt16 vblank; + UInt16 vsyncstart; + UInt16 vsyncend; + + UInt16 timing_h; + UInt16 timing_v; + + UInt8 unknown[6]; +} __attribute__((packed)) vbios_modeline_type3; + +typedef struct { + unsigned char unknown[6]; + + vbios_modeline_type3 modelines[]; +} __attribute__((packed)) vbios_resolution_type3; + +typedef struct { + UInt32 chipset_id; + chipset_type chipset; + bios_type bios; + + UInt32 bios_fd; + char* bios_ptr; + + vbios_mode * mode_table; + UInt32 mode_table_size; + UInt8 b1, b2; + + UInt8 unlocked; +} vbios_map; + + + +void display_map_info(vbios_map*); +vbios_map * open_vbios(chipset_type); +void close_vbios (vbios_map*); +void unlock_vbios(vbios_map*); +void relock_vbios(vbios_map*); +void set_mode(vbios_map*, UInt32, UInt32, UInt32, UInt32, UInt32); +void list_modes(vbios_map *map, UInt32 raw); + +#endif Index: branches/prasys/i386/libsaio/edid.h =================================================================== --- branches/prasys/i386/libsaio/edid.h (revision 0) +++ branches/prasys/i386/libsaio/edid.h (revision 51) @@ -0,0 +1,22 @@ +/* + * edid.h + * + * + * Created by Evan Lojewski on 12/1/09. + * Copyright 2009. All rights reserved. + * + */ + + +#define EDID_BLOCK_SIZE 128 +#define EDID_V1_BLOCKS_TO_GO_OFFSET 126 + +#define SERVICE_REPORT_DDC 0 +#define SERVICE_READ_EDID 1 +#define SERVICE_LAST 1 // Read VDIF has been removed from the spec. + +#define FUNC_GET_EDID 0x4F15 + + +char* readEDID(); +void getResolution(UInt32* x, UInt32* y, UInt32* bp); \ No newline at end of file Index: branches/prasys/i386/libsaio/mindrvr.c =================================================================== --- branches/prasys/i386/libsaio/mindrvr.c (revision 0) +++ branches/prasys/i386/libsaio/mindrvr.c (revision 51) @@ -0,0 +1,2921 @@ +//******************************************************************** +// MINIMUM ATA LOW LEVEL I/O DRIVER -- MINDRVR.C +// +// by Hale Landis (hlandis@ata-atapi.com) +// +// There is no copyright and there are no restrictions on the use +// of this ATA Low Level I/O Driver code. It is distributed to +// help other programmers understand how the ATA device interface +// works and it is distributed without any warranty. Use this +// code at your own risk. +// +// Minimum ATA Driver (MINDRVR) is a subset of ATADRVR. MINDRVR +// has a single header file and a single C file. MINDRVR can +// be used as the starting point for an ATADRVR for an embedded +// system. NOTE all the places in the MINDRVR.H and MINDRVR.C files +// where there is a comment containing the string "!!!". +// +// Use the header file mindrvr.h in any C files that call MINDRVR +// functions. +// +// This code is based on the ATA/ATAPI-4,-5 and -6 standards and +// on interviews with various ATA controller and drive designers. +// +// Note that MINDRVR does not support ATA CHS addressing. +// +// Most of the MINDRVR code is standard C code and should compile +// using any C compiler. It has been tested using Borland C/C++ 4.5. +// +// This C source file is the header file for the driver +// and is used in the MINDRVR.C files and must also be used +// by any program using the MINDRVR code/functions. +//******************************************************************** + +// Added boot-132 support by netkas, 2009 + +#include "mindrvr.h" +#include "io_inline.h" + +//************************************************************** +// +// !!! data that functions outside of MINDRVR must use +// +// Note that there is no actual "interrupt handler" provide in +// MINDRVR. The interrupt handler is usually a small function that +// is very system specific. However, MINDRVR expects that interrupt +// handler function to provide some status data at the time the +// interrupt handler is executed. +// +// In many systems, including PCI bus based systems, when an +// interrupt is received from an ATA controller, the interrupt +// handler must acknowledge the interrupt by reading both the +// ATA/ATAPI device Status register and the controller status +// register. This status must be stored here so that MINDRVR +// can use it. +// +//************************************************************** + +unsigned char int_ata_status; // ATA status read by interrupt handler + +unsigned char int_bmide_status; // BMIDE status read by interrupt handler + +unsigned char int_use_intr_flag = INT_DEFAULT_INTERRUPT_MODE; + +struct REG_CMD_INFO reg_cmd_info; + +int reg_config_info[2]; + +unsigned char * pio_bmide_base_addr; + +unsigned char * pio_reg_addrs[9]; +/* = +{ + PIO_BASE_ADDR1 + 0, // [0] CB_DATA + PIO_BASE_ADDR1 + 1, // [1] CB_FR & CB_ER + PIO_BASE_ADDR1 + 2, // [2] CB_SC + PIO_BASE_ADDR1 + 3, // [3] CB_SN + PIO_BASE_ADDR1 + 4, // [4] CB_CL + PIO_BASE_ADDR1 + 5, // [5] CB_CH + PIO_BASE_ADDR1 + 6, // [6] CB_DH + PIO_BASE_ADDR1 + 7, // [7] CB_CMD & CB_STAT + PIO_BASE_ADDR2 + 0 // [8] CB_DC & CB_ASTAT +} ; +*/ +unsigned char pio_xfer_width = PIO_DEFAULT_XFER_WIDTH; + +//************************************************************** +// +// functions internal and private to MINDRVR +// +//************************************************************** + +static void sub_setup_command( void ); +static void sub_trace_command( void ); +static int sub_select( unsigned char dev ); +static void sub_wait_poll( unsigned char we, unsigned char pe ); + +unsigned char pio_inbyte( unsigned char addr ); +void pio_outbyte( int addr, unsigned char data ); +static unsigned int pio_inword( unsigned char addr ); +static void pio_outword( int addr, unsigned int data ); +static unsigned long pio_indword( unsigned char addr ); +static void pio_outdword( int addr, unsigned long data ); +static void pio_drq_block_in( unsigned char addrDataReg, + unsigned char * bufAddr, + long wordCnt ); +static void pio_drq_block_out( unsigned char addrDataReg, + unsigned char * bufAddr, + long wordCnt ); +static void pio_rep_inbyte( unsigned char addrDataReg, + unsigned char * bufAddr, + long byteCnt ); +static void pio_rep_outbyte( unsigned char addrDataReg, + unsigned char * bufAddr, + long byteCnt ); +static void pio_rep_inword( unsigned char addrDataReg, + unsigned char * bufAddr, + long wordCnt ); +static void pio_rep_outword( unsigned char addrDataReg, + unsigned char * bufAddr, + long wordCnt ); +static void pio_rep_indword( unsigned char addrDataReg, + unsigned char * bufAddr, + long dwordCnt ); +static void pio_rep_outdword( unsigned char addrDataReg, + unsigned char * bufAddr, + long dwordCnt ); + +static unsigned char pio_readBusMstrCmd( void ); +static unsigned char pio_readBusMstrStatus( void ); +static void pio_writeBusMstrCmd( unsigned char x ); +static void pio_writeBusMstrStatus( unsigned char x ); + +static long tmr_cmd_start_time; // command start time +static void tmr_set_timeout( void ); +static int tmr_chk_timeout( void ); + +// This macro provides a small delay that is used in several +// places in the ATA command protocols: + +#define DELAY400NS { pio_inbyte( CB_ASTAT ); pio_inbyte( CB_ASTAT ); \ + pio_inbyte( CB_ASTAT ); pio_inbyte( CB_ASTAT ); } + + +void pio_set_iobase_addr( unsigned int base1, + unsigned int base2, + unsigned int base3 ) + +{ + pio_bmide_base_addr = base3; + pio_reg_addrs[ CB_DATA ] = (unsigned char *)(base1 + 0); // 0 + pio_reg_addrs[ CB_FR ] = (unsigned char *)(base1 + 1); // 1 + pio_reg_addrs[ CB_SC ] = (unsigned char *)(base1 + 2); // 2 + pio_reg_addrs[ CB_SN ] = (unsigned char *)(base1 + 3); // 3 + pio_reg_addrs[ CB_CL ] = (unsigned char *)(base1 + 4); // 4 + pio_reg_addrs[ CB_CH ] = (unsigned char *)(base1 + 5); // 5 + pio_reg_addrs[ CB_DH ] = (unsigned char *)(base1 + 6); // 6 + pio_reg_addrs[ CB_CMD ] = (unsigned char *)(base1 + 7); // 7 + pio_reg_addrs[ CB_DC ] = (unsigned char *)(base2 + 2); // 8 //was + 6 +} + +//************************************************************* +// +// reg_config() - Check the host adapter and determine the +// number and type of drives attached. +// +// This process is not documented by any of the ATA standards. +// +// Infomation is returned by this function is in +// reg_config_info[] -- see MINDRVR.H. +// +//************************************************************* + +int reg_config( void ) + +{ + int numDev = 0; + unsigned char sc; + unsigned char sn; + unsigned char cl; + unsigned char ch; + unsigned char st; + unsigned char dc; + + // setup register values + + dc = (unsigned char) ( int_use_intr_flag ? 0 : CB_DC_NIEN ); + + // reset Bus Master Error bit + + pio_writeBusMstrStatus( BM_SR_MASK_ERR ); + + // assume there are no devices + + reg_config_info[0] = REG_CONFIG_TYPE_NONE; + reg_config_info[1] = REG_CONFIG_TYPE_NONE; + + // set up Device Control register + + pio_outbyte( CB_DC, dc ); + + // lets see if there is a device 0 + + pio_outbyte( CB_DH, CB_DH_DEV0 ); + DELAY400NS; + pio_outbyte( CB_SC, 0x55 ); + pio_outbyte( CB_SN, 0xaa ); + pio_outbyte( CB_SC, 0xaa ); + pio_outbyte( CB_SN, 0x55 ); + pio_outbyte( CB_SC, 0x55 ); + pio_outbyte( CB_SN, 0xaa ); + sc = pio_inbyte( CB_SC ); + sn = pio_inbyte( CB_SN ); + if ( ( sc == 0x55 ) && ( sn == 0xaa ) ) + reg_config_info[0] = REG_CONFIG_TYPE_UNKN; + + // lets see if there is a device 1 + + pio_outbyte( CB_DH, CB_DH_DEV1 ); + DELAY400NS; + pio_outbyte( CB_SC, 0x55 ); + pio_outbyte( CB_SN, 0xaa ); + pio_outbyte( CB_SC, 0xaa ); + pio_outbyte( CB_SN, 0x55 ); + pio_outbyte( CB_SC, 0x55 ); + pio_outbyte( CB_SN, 0xaa ); + sc = pio_inbyte( CB_SC ); + sn = pio_inbyte( CB_SN ); + if ( ( sc == 0x55 ) && ( sn == 0xaa ) ) + reg_config_info[1] = REG_CONFIG_TYPE_UNKN; + + // now we think we know which devices, if any are there, + // so lets try a soft reset (ignoring any errors). + + pio_outbyte( CB_DH, CB_DH_DEV0 ); + DELAY400NS; + reg_reset( 0 ); + + // lets check device 0 again, is device 0 really there? + // is it ATA or ATAPI? + + pio_outbyte( CB_DH, CB_DH_DEV0 ); + DELAY400NS; + sc = pio_inbyte( CB_SC ); + sn = pio_inbyte( CB_SN ); + if ( ( sc == 0x01 ) && ( sn == 0x01 ) ) + { + reg_config_info[0] = REG_CONFIG_TYPE_UNKN; + st = pio_inbyte( CB_STAT ); + cl = pio_inbyte( CB_CL ); + ch = pio_inbyte( CB_CH ); + if ( ( ( cl == 0x14 ) && ( ch == 0xeb ) ) // PATAPI + || + ( ( cl == 0x69 ) && ( ch == 0x96 ) ) // SATAPI + ) + { + reg_config_info[0] = REG_CONFIG_TYPE_ATAPI; + } + else + if ( ( st != 0 ) + && + ( ( ( cl == 0x00 ) && ( ch == 0x00 ) ) // PATA + || + ( ( cl == 0x3c ) && ( ch == 0xc3 ) ) ) // SATA + ) + { + reg_config_info[0] = REG_CONFIG_TYPE_ATA; + } + } + + // lets check device 1 again, is device 1 really there? + // is it ATA or ATAPI? + + pio_outbyte( CB_DH, CB_DH_DEV1 ); + DELAY400NS; + sc = pio_inbyte( CB_SC ); + sn = pio_inbyte( CB_SN ); + if ( ( sc == 0x01 ) && ( sn == 0x01 ) ) + { + reg_config_info[1] = REG_CONFIG_TYPE_UNKN; + st = pio_inbyte( CB_STAT ); + cl = pio_inbyte( CB_CL ); + ch = pio_inbyte( CB_CH ); + if ( ( ( cl == 0x14 ) && ( ch == 0xeb ) ) // PATAPI + || + ( ( cl == 0x69 ) && ( ch == 0x96 ) ) // SATAPI + ) + { + reg_config_info[1] = REG_CONFIG_TYPE_ATAPI; + } + else + if ( ( st != 0 ) + && + ( ( ( cl == 0x00 ) && ( ch == 0x00 ) ) // PATA + || + ( ( cl == 0x3c ) && ( ch == 0xc3 ) ) ) // SATA + ) + { + reg_config_info[1] = REG_CONFIG_TYPE_ATA; + } + } + + // If possible, select a device that exists, try device 0 first. + + if ( reg_config_info[1] != REG_CONFIG_TYPE_NONE ) + { + pio_outbyte( CB_DH, CB_DH_DEV1 ); + DELAY400NS; + numDev ++ ; + } + if ( reg_config_info[0] != REG_CONFIG_TYPE_NONE ) + { + pio_outbyte( CB_DH, CB_DH_DEV0 ); + DELAY400NS; + numDev ++ ; + } + + // BMIDE Error=1? + + if ( pio_readBusMstrStatus() & BM_SR_MASK_ERR ) + { + reg_cmd_info.ec = 78; // yes + } + + // return the number of devices found + + return numDev; +} + +//************************************************************* +// +// reg_reset() - Execute a Software Reset. +// +// See ATA-2 Section 9.2, ATA-3 Section 9.2, ATA-4 Section 8.3. +// +//************************************************************* + +int reg_reset( unsigned char devRtrn ) + +{ + unsigned char sc; + unsigned char sn; + unsigned char status; + unsigned char dc; + + // setup register values + + dc = (unsigned char) ( int_use_intr_flag ? 0 : CB_DC_NIEN ); + + // reset Bus Master Error bit + + pio_writeBusMstrStatus( BM_SR_MASK_ERR ); + + // initialize the command timeout counter + + tmr_set_timeout(); + + // Set and then reset the soft reset bit in the Device Control + // register. This causes device 0 be selected. + + pio_outbyte( CB_DC, (unsigned char) ( dc | CB_DC_SRST ) ); + DELAY400NS; + pio_outbyte( CB_DC, dc ); + DELAY400NS; + + // If there is a device 0, wait for device 0 to set BSY=0. + + if ( reg_config_info[0] != REG_CONFIG_TYPE_NONE ) + { + while ( 1 ) + { + status = pio_inbyte( CB_STAT ); + if ( ( status & CB_STAT_BSY ) == 0 ) + break; + if ( tmr_chk_timeout() ) + { + reg_cmd_info.to = 1; + reg_cmd_info.ec = 1; + break; + } + } + } + + // If there is a device 1, wait until device 1 allows + // register access. + + if ( reg_config_info[1] != REG_CONFIG_TYPE_NONE ) + { + while ( 1 ) + { + pio_outbyte( CB_DH, CB_DH_DEV1 ); + DELAY400NS; + sc = pio_inbyte( CB_SC ); + sn = pio_inbyte( CB_SN ); + if ( ( sc == 0x01 ) && ( sn == 0x01 ) ) + break; + if ( tmr_chk_timeout() ) + { + reg_cmd_info.to = 1; + reg_cmd_info.ec = 2; + break; + } + } + + // Now check if drive 1 set BSY=0. + + if ( reg_cmd_info.ec == 0 ) + { + if ( pio_inbyte( CB_STAT ) & CB_STAT_BSY ) + { + reg_cmd_info.ec = 3; + } + } + } + + // RESET_DONE: + + // We are done but now we must select the device the caller + // requested. This will cause + // the correct data to be returned in reg_cmd_info. + + pio_outbyte( CB_DH, (unsigned char) ( devRtrn ? CB_DH_DEV1 : CB_DH_DEV0 ) ); + DELAY400NS; + + // If possible, select a device that exists, + // try device 0 first. + + if ( reg_config_info[1] != REG_CONFIG_TYPE_NONE ) + { + pio_outbyte( CB_DH, CB_DH_DEV1 ); + DELAY400NS; + } + if ( reg_config_info[0] != REG_CONFIG_TYPE_NONE ) + { + pio_outbyte( CB_DH, CB_DH_DEV0 ); + DELAY400NS; + } + + // BMIDE Error=1? + + if ( pio_readBusMstrStatus() & BM_SR_MASK_ERR ) + { + reg_cmd_info.ec = 78; // yes + } + + // All done. The return values of this function are described in + // MINDRVR.H. + + sub_trace_command(); + if ( reg_cmd_info.ec ) + return 1; + return 0; +} + +//************************************************************* +// +// exec_non_data_cmd() - Execute a non-data command. +// +// This includes the strange ATAPI DEVICE RESET 'command' +// (command code 08H). +// +// Note special handling for Execute Device Diagnostics +// command when there is no device 0. +// +// See ATA-2 Section 9.5, ATA-3 Section 9.5, +// ATA-4 Section 8.8 Figure 12. Also see Section 8.5. +// +//************************************************************* + +static int exec_non_data_cmd( unsigned char dev ); + +static int exec_non_data_cmd( unsigned char dev ) + +{ + unsigned char secCnt; + unsigned char secNum; + unsigned char status; + int polled = 0; + + // reset Bus Master Error bit + + pio_writeBusMstrStatus( BM_SR_MASK_ERR ); + + // Set command time out. + + tmr_set_timeout(); + + // PAY ATTENTION HERE + // If the caller is attempting a Device Reset command, then + // don't do most of the normal stuff. Device Reset has no + // parameters, should not generate an interrupt and it is the + // only command that can be written to the Command register + // when a device has BSY=1 (a very strange command!). Not + // all devices support this command (even some ATAPI devices + // don't support the command. + + if ( reg_cmd_info.cmd != CMD_DEVICE_RESET ) + { + // Select the drive - call the sub_select function. + // Quit now if this fails. + + if ( sub_select( dev ) ) + { + return 1; + } + + // Set up all the registers except the command register. + + sub_setup_command(); + } + + // Start the command by setting the Command register. The drive + // should immediately set BUSY status. + + pio_outbyte( CB_CMD, reg_cmd_info.cmd ); + + // Waste some time by reading the alternate status a few times. + // This gives the drive time to set BUSY in the status register on + // really fast systems. If we don't do this, a slow drive on a fast + // system may not set BUSY fast enough and we would think it had + // completed the command when it really had not even started the + // command yet. + + DELAY400NS; + + // IF + // This is an Exec Dev Diag command (cmd=0x90) + // and there is no device 0 then + // there will be no interrupt. So we must + // poll device 1 until it allows register + // access and then do normal polling of the Status + // register for BSY=0. + // ELSE + // IF + // This is a Dev Reset command (cmd=0x08) then + // there should be no interrupt. So we must + // poll for BSY=0. + // ELSE + // Do the normal wait for interrupt or polling for + // completion. + + if ( ( reg_cmd_info.cmd == CMD_EXECUTE_DEVICE_DIAGNOSTIC ) + && + ( reg_config_info[0] == REG_CONFIG_TYPE_NONE ) + ) + { + polled = 1; + while ( 1 ) + { + pio_outbyte( CB_DH, CB_DH_DEV1 ); + DELAY400NS; + secCnt = pio_inbyte( CB_SC ); + secNum = pio_inbyte( CB_SN ); + if ( ( secCnt == 0x01 ) && ( secNum == 0x01 ) ) + break; + if ( tmr_chk_timeout() ) + { + reg_cmd_info.to = 1; + reg_cmd_info.ec = 24; + break; + } + } + } + else + { + if ( reg_cmd_info.cmd == CMD_DEVICE_RESET ) + { + // Wait for not BUSY -or- wait for time out. + + polled = 1; + sub_wait_poll( 0, 23 ); + } + else + { + // Wait for interrupt -or- wait for not BUSY -or- wait for time out. + + if ( ! int_use_intr_flag ) + polled = 1; + sub_wait_poll( 22, 23 ); + } + } + + // If status was polled or if any error read the status register, + // otherwise get the status that was read by the interrupt handler. + + if ( ( polled ) || ( reg_cmd_info.ec ) ) + status = pio_inbyte( CB_STAT ); + else + status = int_ata_status; + + // Error if BUSY, DEVICE FAULT, DRQ or ERROR status now. + + if ( reg_cmd_info.ec == 0 ) + { + if ( status & ( CB_STAT_BSY | CB_STAT_DF | CB_STAT_DRQ | CB_STAT_ERR ) ) + { + reg_cmd_info.ec = 21; + } + } + + // BMIDE Error=1? + + if ( pio_readBusMstrStatus() & BM_SR_MASK_ERR ) + { + reg_cmd_info.ec = 78; // yes + } + + // NON_DATA_DONE: + + // All done. The return values of this function are described in + // MINDRVR.H. + + sub_trace_command(); + if ( reg_cmd_info.ec ) + return 1; + return 0; +} + +//************************************************************* +// +// reg_non_data_lba28() - Easy way to execute a non-data command +// using an LBA sector address. +// +//************************************************************* + +int reg_non_data_lba28( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lba ) + +{ + + // Setup current command information. + + reg_cmd_info.cmd = cmd; + reg_cmd_info.fr = fr; + reg_cmd_info.sc = sc; + reg_cmd_info.dh = (unsigned char) ( CB_DH_LBA | ( dev ? CB_DH_DEV1 : CB_DH_DEV0 ) ); + reg_cmd_info.dc = (unsigned char) ( int_use_intr_flag ? 0 : CB_DC_NIEN ); + reg_cmd_info.ns = sc; + reg_cmd_info.lbaSize = LBA28; + reg_cmd_info.lbaHigh = 0L; + reg_cmd_info.lbaLow = lba; + + // Execute the command. + + return exec_non_data_cmd( dev ); +} + +//************************************************************* +// +// reg_non_data_lba48() - Easy way to execute a non-data command +// using an LBA sector address. +// +//************************************************************* + +int reg_non_data_lba48( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lbahi, unsigned long lbalo ) + +{ + + // Setup current command infomation. + + reg_cmd_info.cmd = cmd; + reg_cmd_info.fr = fr; + reg_cmd_info.sc = sc; + reg_cmd_info.dh = (unsigned char) ( CB_DH_LBA | ( dev ? CB_DH_DEV1 : CB_DH_DEV0 ) ); + reg_cmd_info.dc = (unsigned char) ( int_use_intr_flag ? 0 : CB_DC_NIEN ); + reg_cmd_info.ns = sc; + reg_cmd_info.lbaSize = LBA48; + reg_cmd_info.lbaHigh = lbahi; + reg_cmd_info.lbaLow = lbalo; + + // Execute the command. + + return exec_non_data_cmd( dev ); +} + +//************************************************************* +// +// exec_pio_data_in_cmd() - Execute a PIO Data In command. +// +// See ATA-2 Section 9.3, ATA-3 Section 9.3, +// ATA-4 Section 8.6 Figure 10. +// +//************************************************************* + +static int exec_pio_data_in_cmd( unsigned char dev, + unsigned char * bufAddr, + long numSect, int multiCnt ); + + +static int exec_pio_data_in_cmd( unsigned char dev, + unsigned char * bufAddr, + long numSect, int multiCnt ) + +{ + unsigned char status; + long wordCnt; + + // reset Bus Master Error bit + + pio_writeBusMstrStatus( BM_SR_MASK_ERR ); + + // Set command time out. + + tmr_set_timeout(); + + // Select the drive - call the sub_select function. + // Quit now if this fails. + + if ( sub_select( dev ) ) + { + return 1; + } + + // Set up all the registers except the command register. + + sub_setup_command(); + + // Start the command by setting the Command register. The drive + // should immediately set BUSY status. + + pio_outbyte( CB_CMD, reg_cmd_info.cmd ); + + // Waste some time by reading the alternate status a few times. + // This gives the drive time to set BUSY in the status register on + // really fast systems. If we don't do this, a slow drive on a fast + // system may not set BUSY fast enough and we would think it had + // completed the command when it really had not even started the + // command yet. + + DELAY400NS; + + // Loop to read each sector. + + while ( 1 ) + { + // READ_LOOP: + // + // NOTE NOTE NOTE ... The primary status register (1f7) MUST NOT be + // read more than ONCE for each sector transferred! When the + // primary status register is read, the drive resets IRQ. The + // alternate status register (3f6) can be read any number of times. + // After interrupt read the the primary status register ONCE + // and transfer the 256 words (REP INSW). AS SOON as BOTH the + // primary status register has been read AND the last of the 256 + // words has been read, the drive is allowed to generate the next + // IRQ (newer and faster drives could generate the next IRQ in + // 50 microseconds or less). If the primary status register is read + // more than once, there is the possibility of a race between the + // drive and the software and the next IRQ could be reset before + // the system interrupt controller sees it. + + // Wait for interrupt -or- wait for not BUSY -or- wait for time out. + + sub_wait_poll( 34, 35 ); + + // If polling or error read the status, otherwise + // get the status that was read by the interrupt handler. + + if ( ( ! int_use_intr_flag ) || ( reg_cmd_info.ec ) ) + status = pio_inbyte( CB_STAT ); + else + status = int_ata_status; + + // If there was a time out error, go to READ_DONE. + + if ( reg_cmd_info.ec ) + break; // go to READ_DONE + + // If BSY=0 and DRQ=1, transfer the data, + // even if we find out there is an error later. + + if ( ( status & ( CB_STAT_BSY | CB_STAT_DRQ ) ) == CB_STAT_DRQ ) + { + + // increment number of DRQ packets + + reg_cmd_info.drqPackets ++ ; + + // determine the number of sectors to transfer + + wordCnt = multiCnt ? multiCnt : 1; + if ( wordCnt > numSect ) + wordCnt = numSect; + wordCnt = wordCnt * 256; + + // Do the REP INSW to read the data for one DRQ block. + + reg_cmd_info.totalBytesXfer += ( wordCnt << 1 ); + pio_drq_block_in( CB_DATA, bufAddr, wordCnt ); + + DELAY400NS; // delay so device can get the status updated + + // Note: The drive should have dropped DATA REQUEST by now. If there + // are more sectors to transfer, BUSY should be active now (unless + // there is an error). + + // Decrement the count of sectors to be transferred + // and increment buffer address. + + numSect = numSect - ( multiCnt ? multiCnt : 1 ); + bufAddr = bufAddr + ( 512 * ( multiCnt ? multiCnt : 1 ) ); + } + + // So was there any error condition? + + if ( status & ( CB_STAT_BSY | CB_STAT_DF | CB_STAT_ERR ) ) + { + reg_cmd_info.ec = 31; + break; // go to READ_DONE + } + + // DRQ should have been set -- was it? + + if ( ( status & CB_STAT_DRQ ) == 0 ) + { + reg_cmd_info.ec = 32; + break; // go to READ_DONE + } + + // If all of the requested sectors have been transferred, make a + // few more checks before we exit. + + if ( numSect < 1 ) + { + // Since the drive has transferred all of the requested sectors + // without error, the drive should not have BUSY, DEVICE FAULT, + // DATA REQUEST or ERROR active now. + + status = pio_inbyte( CB_STAT ); + if ( status & ( CB_STAT_BSY | CB_STAT_DF | CB_STAT_DRQ | CB_STAT_ERR ) ) + { + reg_cmd_info.ec = 33; + break; // go to READ_DONE + } + + // All sectors have been read without error, go to READ_DONE. + + break; // go to READ_DONE + + } + + // This is the end of the read loop. If we get here, the loop is + // repeated to read the next sector. Go back to READ_LOOP. + + } + + // BMIDE Error=1? + + if ( pio_readBusMstrStatus() & BM_SR_MASK_ERR ) + { + reg_cmd_info.ec = 78; // yes + } + + // READ_DONE: + + // All done. The return values of this function are described in + // MINDRVR.H. + + if ( reg_cmd_info.ec ) + return 1; + return 0; +} + +//************************************************************* +// +// reg_pio_data_in_lba28() - Easy way to execute a PIO Data In command +// using an LBA sector address. +// +//************************************************************* + +int reg_pio_data_in_lba28( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lba, + unsigned char * bufAddr, + long numSect, int multiCnt ) + +{ + + reg_cmd_info.cmd = cmd; + reg_cmd_info.fr = fr; + reg_cmd_info.sc = sc; + reg_cmd_info.dh = (unsigned char) ( CB_DH_LBA | ( dev ? CB_DH_DEV1 : CB_DH_DEV0 ) ); + reg_cmd_info.dc = (unsigned char) ( int_use_intr_flag ? 0 : CB_DC_NIEN ); + reg_cmd_info.lbaSize = LBA28; + reg_cmd_info.lbaHigh = 0L; + reg_cmd_info.lbaLow = lba; + + // these commands transfer only 1 sector + if ( ( cmd == CMD_IDENTIFY_DEVICE ) + || ( cmd == CMD_IDENTIFY_DEVICE_PACKET ) + ) + numSect = 1; + + // adjust multiple count + if ( multiCnt & 0x0800 ) + { + // assume caller knows what they are doing + multiCnt &= 0x00ff; + } + else + { + // only Read Multiple uses multiCnt + if ( cmd != CMD_READ_MULTIPLE ) + multiCnt = 1; + } + + reg_cmd_info.ns = numSect; + reg_cmd_info.mc = multiCnt; + + return exec_pio_data_in_cmd( dev, bufAddr, numSect, multiCnt ); +} + +//************************************************************* +// +// reg_pio_data_in_lba48() - Easy way to execute a PIO Data In command +// using an LBA sector address. +// +//************************************************************* + +int reg_pio_data_in_lba48( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lbahi, unsigned long lbalo, + unsigned char * bufAddr, + long numSect, int multiCnt ) + +{ + + reg_cmd_info.cmd = cmd; + reg_cmd_info.fr = fr; + reg_cmd_info.sc = sc; + reg_cmd_info.dh = (unsigned char) ( CB_DH_LBA | ( dev ? CB_DH_DEV1 : CB_DH_DEV0 ) ); + reg_cmd_info.dc = (unsigned char) ( int_use_intr_flag ? 0 : CB_DC_NIEN ); + reg_cmd_info.lbaSize = LBA48; + reg_cmd_info.lbaHigh = lbahi; + reg_cmd_info.lbaLow = lbalo; + + // adjust multiple count + if ( multiCnt & 0x0800 ) + { + // assume caller knows what they are doing + multiCnt &= 0x00ff; + } + else + { + // only Read Multiple Ext uses multiCnt + if ( cmd != CMD_READ_MULTIPLE_EXT ) + multiCnt = 1; + } + + reg_cmd_info.ns = numSect; + reg_cmd_info.mc = multiCnt; + + return exec_pio_data_in_cmd( dev, bufAddr, numSect, multiCnt ); +} + +//************************************************************* +// +// exec_pio_data_out_cmd() - Execute a PIO Data Out command. +// +// See ATA-2 Section 9.4, ATA-3 Section 9.4, +// ATA-4 Section 8.7 Figure 11. +// +//************************************************************* + +static int exec_pio_data_out_cmd( unsigned char dev, + unsigned char * bufAddr, + long numSect, int multiCnt ); + +static int exec_pio_data_out_cmd( unsigned char dev, + unsigned char * bufAddr, + long numSect, int multiCnt ) + +{ + unsigned char status; + int loopFlag = 1; + long wordCnt; + + // reset Bus Master Error bit + + pio_writeBusMstrStatus( BM_SR_MASK_ERR ); + + // Set command time out. + + tmr_set_timeout(); + + // Select the drive - call the sub_select function. + // Quit now if this fails. + + if ( sub_select( dev ) ) + { + return 1; + } + + // Set up all the registers except the command register. + + sub_setup_command(); + + // Start the command by setting the Command register. The drive + // should immediately set BUSY status. + + pio_outbyte( CB_CMD, reg_cmd_info.cmd ); + + // Waste some time by reading the alternate status a few times. + // This gives the drive time to set BUSY in the status register on + // really fast systems. If we don't do this, a slow drive on a fast + // system may not set BUSY fast enough and we would think it had + // completed the command when it really had not even started the + // command yet. + + DELAY400NS; + + // Wait for not BUSY or time out. + // Note: No interrupt is generated for the + // first sector of a write command. + + while ( 1 ) + { + status = pio_inbyte( CB_ASTAT ); + if ( ( status & CB_STAT_BSY ) == 0 ) + break; + if ( tmr_chk_timeout() ) + { + reg_cmd_info.to = 1; + reg_cmd_info.ec = 47; + loopFlag = 0; + break; + } + } + + // This loop writes each sector. + + while ( loopFlag ) + { + // WRITE_LOOP: + // + // NOTE NOTE NOTE ... The primary status register (1f7) MUST NOT be + // read more than ONCE for each sector transferred! When the + // primary status register is read, the drive resets IRQ. The + // alternate status register (3f6) can be read any number of times. + // For correct results, transfer the 256 words (REP OUTSW), wait for + // interrupt and then read the primary status register. AS + // SOON as BOTH the primary status register has been read AND the + // last of the 256 words has been written, the drive is allowed to + // generate the next IRQ (newer and faster drives could generate + // the next IRQ in 50 microseconds or less). If the primary + // status register is read more than once, there is the possibility + // of a race between the drive and the software and the next IRQ + // could be reset before the system interrupt controller sees it. + + // If BSY=0 and DRQ=1, transfer the data, + // even if we find out there is an error later. + + if ( ( status & ( CB_STAT_BSY | CB_STAT_DRQ ) ) == CB_STAT_DRQ ) + { + + // increment number of DRQ packets + + reg_cmd_info.drqPackets ++ ; + + // determine the number of sectors to transfer + + wordCnt = multiCnt ? multiCnt : 1; + if ( wordCnt > numSect ) + wordCnt = numSect; + wordCnt = wordCnt * 256; + + // Do the REP OUTSW to write the data for one DRQ block. + + reg_cmd_info.totalBytesXfer += ( wordCnt << 1 ); + pio_drq_block_out( CB_DATA, bufAddr, wordCnt ); + + DELAY400NS; // delay so device can get the status updated + + // Note: The drive should have dropped DATA REQUEST and + // raised BUSY by now. + + // Decrement the count of sectors to be transferred + // and increment buffer address. + + numSect = numSect - ( multiCnt ? multiCnt : 1 ); + bufAddr = bufAddr + ( 512 * ( multiCnt ? multiCnt : 1 ) ); + } + + // So was there any error condition? + + if ( status & ( CB_STAT_BSY | CB_STAT_DF | CB_STAT_ERR ) ) + { + reg_cmd_info.ec = 41; + break; // go to WRITE_DONE + } + + // DRQ should have been set -- was it? + + if ( ( status & CB_STAT_DRQ ) == 0 ) + { + reg_cmd_info.ec = 42; + break; // go to WRITE_DONE + } + + // Wait for interrupt -or- wait for not BUSY -or- wait for time out. + + sub_wait_poll( 44, 45 ); + + // If polling or error read the status, otherwise + // get the status that was read by the interrupt handler. + + if ( ( ! int_use_intr_flag ) || ( reg_cmd_info.ec ) ) + status = pio_inbyte( CB_STAT ); + else + status = int_ata_status; + + // If there was a time out error, go to WRITE_DONE. + + if ( reg_cmd_info.ec ) + break; // go to WRITE_DONE + + // If all of the requested sectors have been transferred, make a + // few more checks before we exit. + + if ( numSect < 1 ) + { + // Since the drive has transferred all of the sectors without + // error, the drive MUST not have BUSY, DEVICE FAULT, DATA REQUEST + // or ERROR status at this time. + + if ( status & ( CB_STAT_BSY | CB_STAT_DF | CB_STAT_DRQ | CB_STAT_ERR ) ) + { + reg_cmd_info.ec = 43; + break; // go to WRITE_DONE + } + + // All sectors have been written without error, go to WRITE_DONE. + + break; // go to WRITE_DONE + + } + + // + // This is the end of the write loop. If we get here, the loop + // is repeated to write the next sector. Go back to WRITE_LOOP. + + } + + // BMIDE Error=1? + + if ( pio_readBusMstrStatus() & BM_SR_MASK_ERR ) + { + reg_cmd_info.ec = 78; // yes + } + + // WRITE_DONE: + + // All done. The return values of this function are described in + // MINDRVR.H. + + if ( reg_cmd_info.ec ) + return 1; + return 0; +} + +//************************************************************* +// +// reg_pio_data_out_lba28() - Easy way to execute a PIO Data In command +// using an LBA sector address. +// +//************************************************************* + +int reg_pio_data_out_lba28( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lba, + unsigned char * bufAddr, + long numSect, int multiCnt ) + +{ + + reg_cmd_info.cmd = cmd; + reg_cmd_info.fr = fr; + reg_cmd_info.sc = sc; + reg_cmd_info.dh = (unsigned char) ( CB_DH_LBA | ( dev ? CB_DH_DEV1 : CB_DH_DEV0 ) ); + reg_cmd_info.dc = (unsigned char) ( int_use_intr_flag ? 0 : CB_DC_NIEN ); + reg_cmd_info.lbaSize = LBA28; + reg_cmd_info.lbaHigh = 0; + reg_cmd_info.lbaLow = lba; + + // adjust multiple count + if ( multiCnt & 0x0800 ) + { + // assume caller knows what they are doing + multiCnt &= 0x00ff; + } + else + { + // only Write Multiple and CFA Write Multiple W/O Erase uses multiCnt + if ( ( cmd != CMD_WRITE_MULTIPLE ) + && ( cmd != CMD_CFA_WRITE_MULTIPLE_WO_ERASE ) + ) + multiCnt = 1; + } + + reg_cmd_info.ns = numSect; + reg_cmd_info.mc = multiCnt; + + return exec_pio_data_out_cmd( dev, bufAddr, numSect, multiCnt ); +} + +//************************************************************* +// +// reg_pio_data_out_lba48() - Easy way to execute a PIO Data In command +// using an LBA sector address. +// +//************************************************************* + +int reg_pio_data_out_lba48( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lbahi, unsigned long lbalo, + unsigned char * bufAddr, + long numSect, int multiCnt ) + +{ + + reg_cmd_info.cmd = cmd; + reg_cmd_info.fr = fr; + reg_cmd_info.sc = sc; + reg_cmd_info.dh = (unsigned char) ( CB_DH_LBA | ( dev ? CB_DH_DEV1 : CB_DH_DEV0 ) ); + reg_cmd_info.dc = (unsigned char) ( int_use_intr_flag ? 0 : CB_DC_NIEN ); + reg_cmd_info.lbaSize = LBA48; + reg_cmd_info.lbaHigh = lbahi; + reg_cmd_info.lbaLow = lbalo; + + // adjust multiple count + if ( multiCnt & 0x0800 ) + { + // assume caller knows what they are doing + multiCnt &= 0x00ff; + } + else + { + // only Write Multiple Ext uses multiCnt + if ( cmd != CMD_WRITE_MULTIPLE_EXT ) + multiCnt = 1; + } + + reg_cmd_info.ns = numSect; + reg_cmd_info.mc = multiCnt; + + return exec_pio_data_out_cmd( dev, bufAddr, numSect, multiCnt ); +} + +#if INCLUDE_ATAPI_PIO + +//************************************************************* +// +// reg_packet() - Execute an ATAPI Packet (A0H) command. +// +// See ATA-4 Section 9.10, Figure 14. +// +//************************************************************* + +int reg_packet( unsigned char dev, + unsigned int cpbc, + unsigned char * cdbBufAddr, + int dir, + long dpbc, + unsigned char * dataBufAddr ) + +{ + unsigned char status; + unsigned int byteCnt; + long wordCnt; + + // reset Bus Master Error bit + + pio_writeBusMstrStatus( BM_SR_MASK_ERR ); + + // Make sure the command packet size is either 12 or 16 + // and save the command packet size and data. + + cpbc = cpbc < 12 ? 12 : cpbc; + cpbc = cpbc > 12 ? 16 : cpbc; + + // Setup current command information. + + reg_cmd_info.cmd = CMD_PACKET; + reg_cmd_info.fr = 0; + reg_cmd_info.sc = 0; + reg_cmd_info.sn = 0; + reg_cmd_info.cl = (unsigned char) ( dpbc & 0x00ff ); + reg_cmd_info.ch = ( unsigned char) ( ( dpbc & 0xff00 ) >> 8 ); + reg_cmd_info.dh = (unsigned char) ( dev ? CB_DH_DEV1 : CB_DH_DEV0 ); + reg_cmd_info.dc = (unsigned char) ( int_use_intr_flag ? 0 : CB_DC_NIEN ); + + // Set command time out. + + tmr_set_timeout(); + + // Select the drive - call the sub_select function. + // Quit now if this fails. + + if ( sub_select( dev ) ) + { + return 1; + } + + // Set up all the registers except the command register. + + sub_setup_command(); + + // Start the command by setting the Command register. The drive + // should immediately set BUSY status. + + pio_outbyte( CB_CMD, CMD_PACKET ); + + // Waste some time by reading the alternate status a few times. + // This gives the drive time to set BUSY in the status register on + // really fast systems. If we don't do this, a slow drive on a fast + // system may not set BUSY fast enough and we would think it had + // completed the command when it really had not even started the + // command yet. + + DELAY400NS; + + // Command packet transfer... + // Poll Alternate Status for BSY=0. + + while ( 1 ) + { + status = pio_inbyte( CB_ASTAT ); // poll for not busy + if ( ( status & CB_STAT_BSY ) == 0 ) + break; + if ( tmr_chk_timeout() ) // time out yet ? + { + reg_cmd_info.to = 1; + reg_cmd_info.ec = 51; + dir = -1; // command done + break; + } + } + + // Command packet transfer... + // Check for protocol failures... no interrupt here please! + + // Command packet transfer... + // If no error, transfer the command packet. + + if ( reg_cmd_info.ec == 0 ) + { + + // Command packet transfer... + // Read the primary status register and the other ATAPI registers. + + status = pio_inbyte( CB_STAT ); + + // Command packet transfer... + // check status: must have BSY=0, DRQ=1 now + + if ( ( status & ( CB_STAT_BSY | CB_STAT_DRQ | CB_STAT_ERR ) ) + != CB_STAT_DRQ + ) + { + reg_cmd_info.ec = 52; + dir = -1; // command done + } + else + { + // Command packet transfer... + // xfer the command packet (the cdb) + + pio_drq_block_out( CB_DATA, cdbBufAddr, cpbc >> 1 ); + + DELAY400NS; // delay so device can get the status updated + } + } + + // Data transfer loop... + // If there is no error, enter the data transfer loop. + + while ( reg_cmd_info.ec == 0 ) + { + // Data transfer loop... + // Wait for interrupt -or- wait for not BUSY -or- wait for time out. + + sub_wait_poll( 53, 54 ); + + // Data transfer loop... + // If there was a time out error, exit the data transfer loop. + + if ( reg_cmd_info.ec ) + { + dir = -1; // command done + break; + } + + // Data transfer loop... + // If using interrupts get the status read by the interrupt + // handler, otherwise read the status register. + + if ( int_use_intr_flag ) + status = int_ata_status; + else + status = pio_inbyte( CB_STAT ); + + // Data transfer loop... + // Exit the read data loop if the device indicates this + // is the end of the command. + + if ( ( status & ( CB_STAT_BSY | CB_STAT_DRQ ) ) == 0 ) + { + dir = -1; // command done + break; + } + + // Data transfer loop... + // The device must want to transfer data... + // check status: must have BSY=0, DRQ=1 now. + + if ( ( status & ( CB_STAT_BSY | CB_STAT_DRQ ) ) != CB_STAT_DRQ ) + { + reg_cmd_info.ec = 55; + dir = -1; // command done + break; + } + + // Data transfer loop... + // get the byte count, check for zero... + + byteCnt = ( pio_inbyte( CB_CH ) << 8 ) | pio_inbyte( CB_CL ); + if ( byteCnt < 1 ) + { + reg_cmd_info.ec = 59; + dir = -1; // command done + break; + } + + // Data transfer loop... + // increment number of DRQ packets + + reg_cmd_info.drqPackets ++ ; + + // Data transfer loop... + // transfer the data and update the i/o buffer address + // and the number of bytes transfered. + + wordCnt = ( byteCnt >> 1 ) + ( byteCnt & 0x0001 ); + reg_cmd_info.totalBytesXfer += ( wordCnt << 1 ); + if ( dir ) + pio_drq_block_out( CB_DATA, dataBufAddr, wordCnt ); + else + pio_drq_block_in( CB_DATA, dataBufAddr, wordCnt ); + dataBufAddr = dataBufAddr + byteCnt; + + DELAY400NS; // delay so device can get the status updated + } + + // End of command... + // Wait for interrupt or poll for BSY=0, + // but don't do this if there was any error or if this + // was a commmand that did not transfer data. + + if ( ( reg_cmd_info.ec == 0 ) && ( dir >= 0 ) ) + { + sub_wait_poll( 56, 57 ); + } + + // Final status check, only if no previous error. + + if ( reg_cmd_info.ec == 0 ) + { + // Final status check... + // If using interrupts get the status read by the interrupt + // handler, otherwise read the status register. + + if ( int_use_intr_flag ) + status = int_ata_status; + else + status = pio_inbyte( CB_STAT ); + + // Final status check... + // check for any error. + + if ( status & ( CB_STAT_BSY | CB_STAT_DRQ | CB_STAT_ERR ) ) + { + reg_cmd_info.ec = 58; + } + } + + // Done... + + // Final status check + // BMIDE Error=1? + + if ( pio_readBusMstrStatus() & BM_SR_MASK_ERR ) + { + reg_cmd_info.ec = 78; // yes + } + + // All done. The return values of this function are described in + // MINDRVR.H. + + if ( reg_cmd_info.ec ) + return 1; + return 0; +} + +#endif // INCLUDE_ATAPI + +#if INCLUDE_ATA_DMA + +//*********************************************************** +// +// Some notes about PCI bus mastering DMA... +// +// !!! The DMA support in MINDRVR is based on x86 PCI bus mastering +// !!! ATA controller design as described by the T13 document +// !!! '1510 Host Controller Standard' (in sections 1-6). +// +// Note that the T13 1510D document also describes a +// complex DMA engine called ADMA. While ADMA is a good idea it +// will probably never be popular or widely implemented. MINDRVR +// does not support ADMA. +// +// The base address of the Bus Master Control Registers (BMIDE) is +// found in the PCI Configuration space for the ATA controller (at +// offset 0x20 in the config space data). This is normally an I/O +// address. +// +// The BMIDE data is 16 bytes of data starting at the BMIDE base +// address. The first 8 bytes is for the primary ATA channel and +// the second 8 bytes is for the secondary ATA channel. The 8 bytes +// contain a "command" byte and a "status" byte and a 4 byte +// (32-bit) physical memory address pointing to the Physical Region +// Descriptor (PRD) list. Each PRD entry describes an area of +// memory or data buffer for the DMA transfer. A region described +// by a PRD may not cross a 64K byte boundary in physical memory. +// Also, the PRD list must not cross a 64K byte boundary. +// +//*********************************************************** + +//*********************************************************** +// +// pci bus master registers and PRD list buffer, +// see the dma_pci_config() and set_up_xfer() functions. +// +// !!! Note that the PRD buffer is statically allocated here +// !!! but the actual address of the buffer is adjusted by +// !!! the dma_pci_config() function. +// +//*********************************************************** + +static unsigned long * dma_pci_prd_ptr; // current PRD buffer address +static int dma_pci_num_prd; // current number of PRD entries + +static unsigned char statReg; // save BM status reg bits +static unsigned char rwControl; // read/write control bit setting + +#define MAX_TRANSFER_SIZE 262144L // max transfer size (in bytes, + // should be multiple of 65536) + +#define MAX_SEG ((MAX_TRANSFER_SIZE/65536L)+2L) // number physical segments +#define MAX_PRD (MAX_SEG*4L) // number of PRDs required + +#define PRD_BUF_SIZE (48+(2*MAX_PRD*8)) // size of PRD list buffer + +static unsigned char prdBuf[PRD_BUF_SIZE]; // PRD buffer +static unsigned long * prdBufPtr; // first PRD addr + +//*********************************************************** +// +// dma_pci_config() - configure/setup for Read/Write DMA +// +// The caller must call this function before attempting +// to use any ATA or ATAPI commands in PCI DMA mode. +// +// !!! MINDRVR assumes the entire DMA data transfer is contained +// !!! within a single contiguous I/O buffer. You may not need +// !!! the dma_pci_config() function depending on how your system +// !!! allocates the PRD buffer. +// +// !!! This function shows an example of PRD buffer allocation. +// !!! The PRD buffer must be aligned +// !!! on a 8 byte boundary and must not cross a 64K byte +// !!! boundary in memory. +// +//*********************************************************** + +int dma_pci_config( void ) + +{ + unsigned long lw; + + // Set up the PRD entry list buffer address - the PRD entry list + // may not span a 64KB boundary in physical memory. Space is + // allocated (above) for this buffer such that it will be + // aligned on a seqment boundary + // and such that the PRD list will not span a 64KB boundary... + lw = (unsigned long) prdBuf; + // ...move up to an 8 byte boundary. + lw = lw + 15; + lw = lw & 0xfffffff8L; + // ...check for 64KB boundary in the first part of the PRD buffer, + // ...if so just move the buffer to that boundary. + if ( ( lw & 0xffff0000L ) + != + ( ( lw + ( MAX_PRD * 8L ) - 1L ) & 0xffff0000L ) + ) + lw = ( lw + ( MAX_PRD * 8L ) ) & 0xffff0000L; + // ... return the address of the first PRD + dma_pci_prd_ptr = prdBufPtr = (unsigned long *) lw; + // ... return the current number of PRD entries + dma_pci_num_prd = 0; + + // read the BM status reg and save the upper 3 bits. + statReg = (unsigned char) ( pio_readBusMstrStatus() & 0x60 ); + + return 0; +} + +//*********************************************************** +// +// set_up_xfer() -- set up the PRD entry list +// +// !!! MINDRVR assumes the entire DMA data transfer is contained +// !!! within a single contiguous I/O buffer. You may not need +// !!! a much more complex set_up_xfer() function to support +// !!! true scatter/gather lists. +// +// The PRD list must be aligned on an 8 byte boundary and the +// list must not cross a 64K byte boundary in memory. +// +//*********************************************************** + +static int set_up_xfer( int dir, long bc, unsigned char * bufAddr ); + +static int set_up_xfer( int dir, long bc, unsigned char * bufAddr ) + +{ + int numPrd; // number of PRD required + int maxPrd; // max number of PRD allowed + unsigned long temp; + unsigned long phyAddr; // physical memory address + unsigned long * prdPtr; // pointer to PRD entry list + + // disable/stop the dma channel, clear interrupt and error bits + pio_writeBusMstrCmd( BM_CR_MASK_STOP ); + pio_writeBusMstrStatus( (unsigned char) ( statReg | BM_SR_MASK_INT | BM_SR_MASK_ERR ) ); + + // setup to build the PRD list... + // ...max PRDs allowed + maxPrd = (int) MAX_PRD; + // ...PRD buffer address + prdPtr = prdBufPtr; + dma_pci_prd_ptr = prdPtr; + // ... convert I/O buffer address to an physical memory address + phyAddr = (unsigned long) bufAddr; + + // build the PRD list... + // ...PRD entry format: + // +0 to +3 = memory address + // +4 to +5 = 0x0000 (not EOT) or 0x8000 (EOT) + // +6 to +7 = byte count + // ...zero number of PRDs + numPrd = 0; + // ...loop to build each PRD + while ( bc > 0 ) + { + if ( numPrd >= maxPrd ) + return 1; + // set this PRD's address + prdPtr[0] = phyAddr; + // set count for this PRD + temp = 65536L; // max PRD length + if ( temp > bc ) // count to large? + temp = bc; // yes - use actual count + // check if count will fit + phyAddr = phyAddr + temp; + if ( ( phyAddr & 0xffff0000L ) != ( prdPtr[0] & 0xffff0000L ) ) + { + phyAddr = phyAddr & 0xffff0000L; + temp = phyAddr - prdPtr[0]; + } + // set this PRD's count + prdPtr[1] = temp & 0x0000ffffL; + // update byte count + bc = bc - temp; + // set the end bit in the prd list + if ( bc < 1 ) + prdPtr[1] = prdPtr[1] | 0x80000000L; + prdPtr ++ ; + prdPtr ++ ; + numPrd ++ ; + } + + // return the current PRD list size and + // write into BMIDE PRD address registers. + + dma_pci_num_prd = numPrd; + * (unsigned long *) (pio_bmide_base_addr + BM_PRD_ADDR_LOW ) + = (unsigned long) prdBufPtr; + + // set the read/write control: + // PCI reads for ATA Write DMA commands, + // PCI writes for ATA Read DMA commands. + + if ( dir ) + rwControl = BM_CR_MASK_READ; // ATA Write DMA + else + rwControl = BM_CR_MASK_WRITE; // ATA Read DMA + pio_writeBusMstrCmd( rwControl ); + return 0; +} + +//*********************************************************** +// +// exec_pci_ata_cmd() - PCI Bus Master for ATA R/W DMA commands +// +//*********************************************************** + +static int exec_pci_ata_cmd( unsigned char dev, + unsigned char * bufAddr, + long numSect ); + +static int exec_pci_ata_cmd( unsigned char dev, + unsigned char * bufAddr, + long numSect ) + +{ + unsigned char status; + + // Quit now if the command is incorrect. + + if ( ( reg_cmd_info.cmd != CMD_READ_DMA ) + && ( reg_cmd_info.cmd != CMD_READ_DMA_EXT ) + && ( reg_cmd_info.cmd != CMD_WRITE_DMA ) + && ( reg_cmd_info.cmd != CMD_WRITE_DMA_EXT ) ) + { + reg_cmd_info.ec = 77; + return 1; + } + + // Set up the dma transfer + + if ( set_up_xfer( ( reg_cmd_info.cmd == CMD_WRITE_DMA ) + || + ( reg_cmd_info.cmd == CMD_WRITE_DMA_EXT ), + numSect * 512L, bufAddr ) ) + { + reg_cmd_info.ec = 61; + return 1; + } + + // Set command time out. + + tmr_set_timeout(); + + // Select the drive - call the sub_select function. + // Quit now if this fails. + + if ( sub_select( dev ) ) + { + return 1; + } + + // Set up all the registers except the command register. + + sub_setup_command(); + + // Start the command by setting the Command register. The drive + // should immediately set BUSY status. + + pio_outbyte( CB_CMD, reg_cmd_info.cmd ); + + // The drive should start executing the command including any + // data transfer. + + // Data transfer... + // read the BMIDE regs + // enable/start the dma channel. + // read the BMIDE regs again + + pio_readBusMstrCmd(); + pio_readBusMstrStatus(); + pio_writeBusMstrCmd( (unsigned char) ( rwControl | BM_CR_MASK_START ) ); + pio_readBusMstrCmd(); + pio_readBusMstrStatus(); + + // Data transfer... + // the device and dma channel transfer the data here while we start + // checking for command completion... + // wait for the PCI BM Interrupt=1 (see ATAIOINT.C)... + + if ( tmr_chk_timeout() ) // time out ? + { + reg_cmd_info.to = 1; + reg_cmd_info.ec = 73; + } + + // End of command... + // disable/stop the dma channel + + status = int_bmide_status; // read BM status + status &= ~ BM_SR_MASK_ACT; // ignore Active bit + pio_writeBusMstrCmd( BM_CR_MASK_STOP ); // shutdown DMA + pio_readBusMstrCmd(); // read BM cmd (just for trace) + status |= pio_readBusMstrStatus(); // read BM status again + + if ( reg_cmd_info.ec == 0 ) + { + if ( status & BM_SR_MASK_ERR ) // bus master error? + { + reg_cmd_info.ec = 78; // yes + } + } + if ( reg_cmd_info.ec == 0 ) + { + if ( status & BM_SR_MASK_ACT ) // end of PRD list? + { + reg_cmd_info.ec = 71; // no + } + } + + // End of command... + // If no error use the Status register value that was read + // by the interrupt handler. If there was an error + // read the Status register because it may not have been + // read by the interrupt handler. + + if ( reg_cmd_info.ec ) + status = pio_inbyte( CB_STAT ); + else + status = int_ata_status; + + // Final status check... + // if no error, check final status... + // Error if BUSY, DEVICE FAULT, DRQ or ERROR status now. + + if ( reg_cmd_info.ec == 0 ) + { + if ( status & ( CB_STAT_BSY | CB_STAT_DF | CB_STAT_DRQ | CB_STAT_ERR ) ) + { + reg_cmd_info.ec = 74; + } + } + + // Final status check... + // if any error, update total bytes transferred. + + if ( reg_cmd_info.ec == 0 ) + reg_cmd_info.totalBytesXfer = numSect * 512L; + else + reg_cmd_info.totalBytesXfer = 0L; + + // All done. The return values of this function are described in + // MINDRVR.H. + + if ( reg_cmd_info.ec ) + return 1; + return 0; +} + +//*********************************************************** +// +// dma_pci_lba28() - DMA in PCI Multiword for ATA R/W DMA +// +//*********************************************************** + +int dma_pci_lba28( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lba, + unsigned char * bufAddr, + long numSect ) + +{ + + // Setup current command information. + + reg_cmd_info.cmd = cmd; + reg_cmd_info.fr = fr; + reg_cmd_info.sc = sc; + reg_cmd_info.dh = (unsigned char) ( CB_DH_LBA | ( dev ? CB_DH_DEV1 : CB_DH_DEV0 ) ); + reg_cmd_info.dc = 0x00; // nIEN=0 required on PCI ! + reg_cmd_info.ns = numSect; + reg_cmd_info.lbaSize = LBA28; + reg_cmd_info.lbaHigh = 0L; + reg_cmd_info.lbaLow = lba; + + // Execute the command. + + return exec_pci_ata_cmd( dev, bufAddr, numSect ); +} + +//*********************************************************** +// +// dma_pci_lba48() - DMA in PCI Multiword for ATA R/W DMA +// +//*********************************************************** + +int dma_pci_lba48( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lbahi, unsigned long lbalo, + unsigned char * bufAddr, + long numSect ) + +{ + + // Setup current command information. + + reg_cmd_info.cmd = cmd; + reg_cmd_info.fr = fr; + reg_cmd_info.sc = sc; + reg_cmd_info.dh = (unsigned char) ( CB_DH_LBA | ( dev ? CB_DH_DEV1 : CB_DH_DEV0 ) ); + reg_cmd_info.dc = 0x00; // nIEN=0 required on PCI ! + reg_cmd_info.ns = numSect; + reg_cmd_info.lbaSize = LBA48; + reg_cmd_info.lbaHigh = lbahi; + reg_cmd_info.lbaLow = lbalo; + + // Execute the command. + + return exec_pci_ata_cmd( dev, bufAddr, numSect ); +} + +#endif // INCLUDE_ATA_DMA + +#if INCLUDE_ATAPI_DMA + +//*********************************************************** +// +// dma_pci_packet() - PCI Bus Master for ATAPI Packet command +// +//*********************************************************** + +int dma_pci_packet( unsigned char dev, + unsigned int cpbc, + unsigned char * cdbBufAddr, + int dir, + long dpbc, + unsigned char * dataBufAddr ) + +{ + unsigned char status; + + // Make sure the command packet size is either 12 or 16 + // and save the command packet size and data. + + cpbc = cpbc < 12 ? 12 : cpbc; + cpbc = cpbc > 12 ? 16 : cpbc; + + // Setup current command information. + + reg_cmd_info.cmd = CMD_PACKET; + reg_cmd_info.fr = 0x01; // packet DMA mode ! + reg_cmd_info.sc = 0; + reg_cmd_info.sn = 0; + reg_cmd_info.cl = 0; // no Byte Count Limit in DMA ! + reg_cmd_info.ch = 0; // no Byte Count Limit in DMA ! + reg_cmd_info.dh = (unsigned char) ( dev ? CB_DH_DEV1 : CB_DH_DEV0 ); + reg_cmd_info.dc = 0x00; // nIEN=0 required on PCI ! + + // the data packet byte count must be even + // and must not be zero + + if ( dpbc & 1L ) + dpbc ++ ; + if ( dpbc < 2L ) + dpbc = 2L; + + // Set up the dma transfer + + if ( set_up_xfer( dir, dpbc, dataBufAddr ) ) + { + reg_cmd_info.ec = 61; + return 1; + } + + // Set command time out. + + tmr_set_timeout(); + + // Select the drive - call the reg_select function. + // Quit now if this fails. + + if ( sub_select( dev ) ) + { + return 1; + } + + // Set up all the registers except the command register. + + sub_setup_command(); + + // Start the command by setting the Command register. The drive + // should immediately set BUSY status. + + pio_outbyte( CB_CMD, CMD_PACKET ); + + // Waste some time by reading the alternate status a few times. + // This gives the drive time to set BUSY in the status register on + // really fast systems. If we don't do this, a slow drive on a fast + // system may not set BUSY fast enough and we would think it had + // completed the command when it really had not started the + // command yet. + + DELAY400NS; + + // Command packet transfer... + // Poll Alternate Status for BSY=0. + + while ( 1 ) + { + status = pio_inbyte( CB_ASTAT ); // poll for not busy + if ( ( status & CB_STAT_BSY ) == 0 ) + break; + if ( tmr_chk_timeout() ) // time out yet ? + { + reg_cmd_info.to = 1; + reg_cmd_info.ec = 75; + break; + } + } + + // Command packet transfer... + // Check for protocol failures... no interrupt here please! + + // Command packet transfer... + // If no error, transfer the command packet. + + if ( reg_cmd_info.ec == 0 ) + { + + // Command packet transfer... + // Read the primary status register and the other ATAPI registers. + + status = pio_inbyte( CB_STAT ); + + // Command packet transfer... + // check status: must have BSY=0, DRQ=1 now + + if ( ( status & ( CB_STAT_BSY | CB_STAT_DRQ | CB_STAT_ERR ) ) + != CB_STAT_DRQ + ) + { + reg_cmd_info.ec = 76; + } + else + { + + // Command packet transfer... + // xfer the command packet (the cdb) + + pio_drq_block_out( CB_DATA, cdbBufAddr, cpbc >> 1 ); + } + } + + // Data transfer... + // The drive should start executing the command + // including any data transfer. + // If no error, set up and start the DMA, + // and wait for the DMA to complete. + + if ( reg_cmd_info.ec == 0 ) + { + + // Data transfer... + // read the BMIDE regs + // enable/start the dma channel. + // read the BMIDE regs again + + pio_readBusMstrCmd(); + pio_readBusMstrStatus(); + pio_writeBusMstrCmd( (unsigned char) ( rwControl | BM_CR_MASK_START ) ); + pio_readBusMstrCmd(); + pio_readBusMstrStatus(); + + // Data transfer... + // the device and dma channel transfer the data here while we start + // checking for command completion... + // wait for the PCI BM Active=0 and Interrupt=1 or PCI BM Error=1... + + if ( tmr_chk_timeout() ) // time out ? + { + reg_cmd_info.to = 1; + reg_cmd_info.ec = 73; + } + + // End of command... + // disable/stop the dma channel + + status = int_bmide_status; // read BM status + status &= ~ BM_SR_MASK_ACT; // ignore Active bit + pio_writeBusMstrCmd( BM_CR_MASK_STOP ); // shutdown DMA + pio_readBusMstrCmd(); // read BM cmd (just for trace) + status |= pio_readBusMstrStatus(); // read BM statu again + } + + if ( reg_cmd_info.ec == 0 ) + { + if ( status & ( BM_SR_MASK_ERR ) ) // bus master error? + { + reg_cmd_info.ec = 78; // yes + } + if ( ( status & BM_SR_MASK_ACT ) ) // end of PRD list? + { + reg_cmd_info.ec = 71; // no + } + } + + // End of command... + // If no error use the Status register value that was read + // by the interrupt handler. If there was an error + // read the Status register because it may not have been + // read by the interrupt handler. + + if ( reg_cmd_info.ec ) + status = pio_inbyte( CB_STAT ); + else + status = int_ata_status; + + // Final status check... + // if no error, check final status... + // Error if BUSY, DRQ or ERROR status now. + + if ( reg_cmd_info.ec == 0 ) + { + if ( status & ( CB_STAT_BSY | CB_STAT_DRQ | CB_STAT_ERR ) ) + { + reg_cmd_info.ec = 74; + } + } + + + // Final status check... + // if any error, update total bytes transferred. + + if ( reg_cmd_info.ec == 0 ) + reg_cmd_info.totalBytesXfer = dpbc; + else + reg_cmd_info.totalBytesXfer = 0L; + + // All done. The return values of this function are described in + // MINDRVR.H. + + if ( reg_cmd_info.ec ) + return 1; + return 0; +} + +#endif // INCLUDE_ATAPI_DMA + +//************************************************************* +// +// sub_setup_command() -- setup the command parameters +// in FR, SC, SN, CL, CH and DH. +// +//************************************************************* + +static void sub_setup_command( void ) + +{ + + // output DevCtrl - same for all devices and commands + pio_outbyte( CB_DC, reg_cmd_info.dc ); + + // output command parameters + if ( reg_cmd_info.lbaSize == LBA28 ) + { + // in ATA LBA28 mode + pio_outbyte( CB_FR, (unsigned char) reg_cmd_info.fr ); + pio_outbyte( CB_SC, (unsigned char) reg_cmd_info.sc ); + pio_outbyte( CB_SN, (unsigned char) reg_cmd_info.lbaLow ); + pio_outbyte( CB_CL, (unsigned char) ( reg_cmd_info.lbaLow >> 8 ) ); + pio_outbyte( CB_CH, (unsigned char) ( reg_cmd_info.lbaLow >> 16 ) ); + pio_outbyte( CB_DH, (unsigned char) ( ( reg_cmd_info.dh & 0xf0 ) + | ( ( reg_cmd_info.lbaLow >> 24 ) & 0x0f ) ) ); + } + else + if ( reg_cmd_info.lbaSize == LBA48 ) + { + // in ATA LBA48 mode + pio_outbyte( CB_FR, (unsigned char) ( reg_cmd_info.fr >> 8 ) ); + pio_outbyte( CB_SC, (unsigned char) ( reg_cmd_info.sc >> 8 ) ); + pio_outbyte( CB_SN, (unsigned char) ( reg_cmd_info.lbaLow >> 24 ) ); + pio_outbyte( CB_CL, (unsigned char) reg_cmd_info.lbaHigh ); + pio_outbyte( CB_CH, (unsigned char) ( reg_cmd_info.lbaHigh >> 8 ) ); + pio_outbyte( CB_FR, (unsigned char) reg_cmd_info.fr ); + pio_outbyte( CB_SC, (unsigned char) reg_cmd_info.sc ); + pio_outbyte( CB_SN, (unsigned char) reg_cmd_info.lbaLow ); + pio_outbyte( CB_CL, (unsigned char) ( reg_cmd_info.lbaLow >> 8 ) ); + pio_outbyte( CB_CH, (unsigned char) ( reg_cmd_info.lbaLow >> 16 ) ); + pio_outbyte( CB_DH, reg_cmd_info.dh ); + } + else + { + // for ATAPI PACKET command + pio_outbyte( CB_FR, (unsigned char) reg_cmd_info.fr ); + pio_outbyte( CB_SC, (unsigned char) reg_cmd_info.sc ); + pio_outbyte( CB_SN, (unsigned char) reg_cmd_info.sn ); + pio_outbyte( CB_CL, (unsigned char) reg_cmd_info.cl ); + pio_outbyte( CB_CH, (unsigned char) reg_cmd_info.ch ); + pio_outbyte( CB_DH, reg_cmd_info.dh ); + } +} + +//************************************************************* +// +// sub_trace_command() -- trace the end of a command. +// +//************************************************************* + +static void sub_trace_command( void ) + +{ + + reg_cmd_info.st = pio_inbyte( CB_STAT ); + reg_cmd_info.as = pio_inbyte( CB_ASTAT ); + reg_cmd_info.er = pio_inbyte( CB_ERR ); + +// !!! if you want to read back the other device registers +// !!! at the end of a command then this is the place to do +// !!! it. The code here is just and example of out this is +// !!! done on a little endian system like an x86. + +#if 0 // read back other registers + + { + unsigned long lbaHigh; + unsigned long lbaLow; + unsigned char sc48[2]; + unsigned char lba48[8]; + + lbaHigh = 0; + lbaLow = 0; + if ( reg_cmd_info.lbaSize == LBA48 ) + { + // read back ATA LBA48... + sc48[0] = pio_inbyte( CB_SC ); + lba48[0] = pio_inbyte( CB_SN ); + lba48[1] = pio_inbyte( CB_CL ); + lba48[2] = pio_inbyte( CB_CH ); + pio_outbyte( CB_DC, CB_DC_HOB ); + sc48[1] = pio_inbyte( CB_SC ); + lba48[3] = pio_inbyte( CB_SN ); + lba48[4] = pio_inbyte( CB_CL ); + lba48[5] = pio_inbyte( CB_CH ); + lba48[6] = 0; + lba48[7] = 0; + lbaHigh = * (unsigned long *) ( lba48 + 4 ); + lbaLow = * (unsigned long *) ( lba48 + 0 ); + } + else + if ( reg_cmd_info.lbaSize == LBA28 ) + { + // read back ATA LBA28 + lbaLow = pio_inbyte( CB_DH ); + lbaLow = lbaLow << 8; + lbaLow = lbaLow | pio_inbyte( CB_CH ); + lbaLow = lbaLow << 8; + lbaLow = lbaLow | pio_inbyte( CB_CL ); + lbaLow = lbaLow << 8; + lbaLow = lbaLow | pio_inbyte( CB_SN ); + } + else + { + // really no reason to read back for ATAPI + } + } + +#endif // read back other registers + +} + +//************************************************************* +// +// sub_select() - function used to select a drive. +// +// Function to select a drive making sure that BSY=0 and DRQ=0. +// +//************************************************************** + +static int sub_select( unsigned char dev ) + +{ + unsigned char status; + + // PAY ATTENTION HERE + // The caller may want to issue a command to a device that doesn't + // exist (for example, Exec Dev Diag), so if we see this, + // just select that device, skip all status checking and return. + // We assume the caller knows what they are doing! + + if ( reg_config_info[dev] < REG_CONFIG_TYPE_ATA ) + { + // select the device and return + + pio_outbyte( CB_DH, (unsigned char) ( dev ? CB_DH_DEV1 : CB_DH_DEV0 ) ); + DELAY400NS; + return 0; + } + + // The rest of this is the normal ATA stuff for device selection + // and we don't expect the caller to be selecting a device that + // does not exist. + // We don't know which drive is currently selected but we should + // wait BSY=0 and DRQ=0. Normally both BSY=0 and DRQ=0 + // unless something is very wrong! + + while ( 1 ) + { + status = pio_inbyte( CB_STAT ); + if ( ( status & ( CB_STAT_BSY | CB_STAT_DRQ ) ) == 0 ) + break; + if ( tmr_chk_timeout() ) + { + reg_cmd_info.to = 1; + reg_cmd_info.ec = 11; + reg_cmd_info.st = status; + reg_cmd_info.as = pio_inbyte( CB_ASTAT ); + reg_cmd_info.er = pio_inbyte( CB_ERR ); + return 1; + } + } + + // Here we select the drive we really want to work with by + // setting the DEV bit in the Drive/Head register. + + pio_outbyte( CB_DH, (unsigned char) ( dev ? CB_DH_DEV1 : CB_DH_DEV0 ) ); + DELAY400NS; + + // Wait for the selected device to have BSY=0 and DRQ=0. + // Normally the drive should be in this state unless + // something is very wrong (or initial power up is still in + // progress). + + while ( 1 ) + { + status = pio_inbyte( CB_STAT ); + if ( ( status & ( CB_STAT_BSY | CB_STAT_DRQ ) ) == 0 ) + break; + if ( tmr_chk_timeout() ) + { + reg_cmd_info.to = 1; + reg_cmd_info.ec = 12; + reg_cmd_info.st = status; + reg_cmd_info.as = pio_inbyte( CB_ASTAT ); + reg_cmd_info.er = pio_inbyte( CB_ERR ); + return 1; + } + } + + // All done. The return values of this function are described in + // ATAIO.H. + + if ( reg_cmd_info.ec ) + return 1; + return 0; +} + +//************************************************************* +// +// sub_wait_poll() - wait for interrupt or poll for BSY=0 +// +//************************************************************* + +static void sub_wait_poll( unsigned char we, unsigned char pe ) + +{ + unsigned char status; + + // Wait for interrupt -or- wait for not BUSY -or- wait for time out. + + if ( we && int_use_intr_flag ) + { + if ( tmr_chk_timeout() ) // time out ? + { + reg_cmd_info.to = 1; + reg_cmd_info.ec = we; + } + } + else + { + while ( 1 ) + { + status = pio_inbyte( CB_ASTAT ); // poll for not busy + if ( ( status & CB_STAT_BSY ) == 0 ) + break; + if ( tmr_chk_timeout() ) // time out yet ? + { + reg_cmd_info.to = 1; + reg_cmd_info.ec = pe; + break; + } + } + } +} + +//*********************************************************** +// +// functions used to read/write the BMIDE registers +// +//*********************************************************** + +static unsigned char pio_readBusMstrCmd( void ) + +{ + unsigned char x; + + if ( ! pio_bmide_base_addr ) + return 0; + x = inb (pio_bmide_base_addr + BM_COMMAND_REG ); + return x; +} + + +static unsigned char pio_readBusMstrStatus( void ) + +{ + unsigned char x; + + if ( ! pio_bmide_base_addr ) + return 0; + x = inb( pio_bmide_base_addr + BM_STATUS_REG ); + return x; +} + + +static void pio_writeBusMstrCmd( unsigned char x ) + +{ + + if ( ! pio_bmide_base_addr ) + return; + outb(( pio_bmide_base_addr + BM_COMMAND_REG ), x); +} + + +static void pio_writeBusMstrStatus( unsigned char x ) + +{ + + if ( ! pio_bmide_base_addr ) + return; + outb(( pio_bmide_base_addr + BM_STATUS_REG ), x); +} + +//************************************************************* +// +// These functions do basic IN/OUT of byte and word values: +// +// pio_inbyte() +// pio_outbyte() +// pio_inword() +// pio_outword() +// +//************************************************************* +/* +static unsigned char pio_inbyte( unsigned char addr ) + +{ + + //!!! read an 8-bit ATA register + + return * pio_reg_addrs[ addr ]; +} + +//************************************************************* + +static void pio_outbyte( int addr, unsigned char data ) + +{ + + //!!! write an 8-bit ATA register + + * pio_reg_addrs[ addr ] = data; +} + +//************************************************************* + +static unsigned int pio_inword( unsigned char addr ) + +{ + + //!!! read an 8-bit ATA register (usually the ATA Data register) + + return * ( (unsigned int *) pio_reg_addrs[ addr ] ); +} + +//************************************************************* + +static void pio_outword( int addr, unsigned int data ) + +{ + + //!!! Write an 8-bit ATA register (usually the ATA Data register) + + * ( (unsigned int *) pio_reg_addrs[ addr ] ) = data; +} + +//************************************************************* + +static unsigned long pio_indword( unsigned char addr ) + +{ + + //!!! read an 8-bit ATA register (usually the ATA Data register) + + return * ( (unsigned long *) pio_reg_addrs[ addr ] ); +} + +//************************************************************* + +static void pio_outdword( int addr, unsigned long data ) + +{ + + //!!! Write an 8-bit ATA register (usually the ATA Data register) + + * ( (unsigned long *) pio_reg_addrs[ addr ] ) = data; +} + + +*/ +unsigned char pio_inbyte( unsigned char addr ) + +{ + + //!!! read an 8-bit ATA register + + return inb((unsigned int)pio_reg_addrs[ addr ]); +} + +//************************************************************* + +void pio_outbyte( int addr, unsigned char data ) + +{ + + //!!! write an 8-bit ATA register + + outb((unsigned int)pio_reg_addrs[ addr ], data); +} + +//************************************************************* + +static unsigned int pio_inword( unsigned char addr ) + +{ + + //!!! read an 8-bit ATA register (usually the ATA Data register) + + return inw((unsigned int)pio_reg_addrs[ addr ] ); +} + +//************************************************************* + +static void pio_outword( int addr, unsigned int data ) + +{ + + //!!! Write an 8-bit ATA register (usually the ATA Data register) + + outw((unsigned int)pio_reg_addrs[ addr ], data); +} + +//************************************************************* + +static unsigned long pio_indword( unsigned char addr ) + +{ + + //!!! read an 8-bit ATA register (usually the ATA Data register) + + return inl((unsigned int)pio_reg_addrs[ addr ] ); +} + +//************************************************************* + +static void pio_outdword( int addr, unsigned long data ) + +{ + + //!!! Write an 8-bit ATA register (usually the ATA Data register) + + outl((unsigned int)pio_reg_addrs[ addr ], data); +} + +//************************************************************* +// +// These functions are normally used to transfer DRQ blocks: +// +// pio_drq_block_in() +// pio_drq_block_out() +// +//************************************************************* + +// Note: pio_drq_block_in() is the primary way perform PIO +// Data In transfers. It will handle 8-bit, 16-bit and 32-bit +// I/O based data transfers. + +static void pio_drq_block_in( unsigned char addrDataReg, + unsigned char * bufAddr, + long wordCnt ) + +{ + + // NOTE: wordCnt is the size of a DRQ data block/packet + // in words. The maximum value of wordCnt is normally: + // a) For ATA, 16384 words or 32768 bytes (64 sectors, + // only with READ/WRITE MULTIPLE commands), + // b) For ATAPI, 32768 words or 65536 bytes + // (actually 65535 bytes plus a pad byte). + + { + int pxw; + long wc; + + // adjust pio_xfer_width - don't use DWORD if wordCnt is odd. + + pxw = pio_xfer_width; + if ( ( pxw == 32 ) && ( wordCnt & 0x00000001L ) ) + pxw = 16; + + // Data transfer using INS instruction. + // Break the transfer into chunks of 32768 or fewer bytes. + + while ( wordCnt > 0 ) + { + if ( wordCnt > 16384L ) + wc = 16384; + else + wc = wordCnt; + if ( pxw == 8 ) + { + // do REP INS + pio_rep_inbyte( addrDataReg, bufAddr, wc * 2L ); + } + else + if ( pxw == 32 ) + { + // do REP INSD + pio_rep_indword( addrDataReg, bufAddr, wc / 2L ); + } + else + { + // do REP INSW + pio_rep_inword( addrDataReg, bufAddr, wc ); + } + bufAddr = bufAddr + ( wc * 2 ); + wordCnt = wordCnt - wc; + } + } + + return; +} + +//************************************************************* + +// Note: pio_drq_block_out() is the primary way perform PIO +// Data Out transfers. It will handle 8-bit, 16-bit and 32-bit +// I/O based data transfers. + +static void pio_drq_block_out( unsigned char addrDataReg, + unsigned char * bufAddr, + long wordCnt ) + +{ + + // NOTE: wordCnt is the size of a DRQ data block/packet + // in words. The maximum value of wordCnt is normally: + // a) For ATA, 16384 words or 32768 bytes (64 sectors, + // only with READ/WRITE MULTIPLE commands), + // b) For ATAPI, 32768 words or 65536 bytes + // (actually 65535 bytes plus a pad byte). + + { + int pxw; + long wc; + + // adjust pio_xfer_width - don't use DWORD if wordCnt is odd. + + pxw = pio_xfer_width; + if ( ( pxw == 32 ) && ( wordCnt & 0x00000001L ) ) + pxw = 16; + + // Data transfer using OUTS instruction. + // Break the transfer into chunks of 32768 or fewer bytes. + + while ( wordCnt > 0 ) + { + if ( wordCnt > 16384L ) + wc = 16384; + else + wc = wordCnt; + if ( pxw == 8 ) + { + // do REP OUTS + pio_rep_outbyte( addrDataReg, bufAddr, wc * 2L ); + } + else + if ( pxw == 32 ) + { + // do REP OUTSD + pio_rep_outdword( addrDataReg, bufAddr, wc / 2L ); + } + else + { + // do REP OUTSW + pio_rep_outword( addrDataReg, bufAddr, wc ); + } + bufAddr = bufAddr + ( wc * 2 ); + wordCnt = wordCnt - wc; + } + } + + return; +} + +//************************************************************* +// +// These functions transfer PIO DRQ data blocks through the ATA +// Data register. On an x86 these functions would use the +// REP INS and REP OUTS instructions. +// +// pio_rep_inbyte() +// pio_rep_outbyte() +// pio_rep_inword() +// pio_rep_outword() +// pio_rep_indword() +// pio_rep_outdword() +// +// These functions can be called directly but usually they +// are called by the pio_drq_block_in() and pio_drq_block_out() +// functions to perform I/O mode transfers. See the +// pio_xfer_width variable! +// +//************************************************************* + +static void pio_rep_inbyte( unsigned char addrDataReg, + unsigned char * bufAddr, + long byteCnt ) + +{ + + // Warning: Avoid calling this function with + // byteCnt > 32768 (transfers 32768 bytes). + // that bufOff is a value between 0 and 15 (0xf). + + //!!! repeat read an 8-bit register (ATA Data register when + //!!! ATA status is BSY=0 DRQ=1). For example: + + while ( byteCnt > 0 ) + { + * bufAddr = pio_inbyte( addrDataReg ); + bufAddr ++ ; + byteCnt -- ; + } +} + +//************************************************************* + +static void pio_rep_outbyte( unsigned char addrDataReg, + unsigned char * bufAddr, + long byteCnt ) + +{ + + // Warning: Avoid calling this function with + // byteCnt > 32768 (transfers 32768 bytes). + // that bufOff is a value between 0 and 15 (0xf). + + //!!! repeat write an 8-bit register (ATA Data register when + //!!! ATA status is BSY=0 DRQ=1). For example: + + while ( byteCnt > 0 ) + { + pio_outbyte( addrDataReg, * bufAddr ); + bufAddr ++ ; + byteCnt -- ; + } +} + +//************************************************************* + +static void pio_rep_inword( unsigned char addrDataReg, + unsigned char * bufAddr, + long wordCnt ) + +{ + + // Warning: Avoid calling this function with + // wordCnt > 16384 (transfers 32768 bytes). + + //!!! repeat read a 16-bit register (ATA Data register when + //!!! ATA status is BSY=0 DRQ=1). For example: + + while ( wordCnt > 0 ) + { + * (unsigned int *) bufAddr = pio_inword( addrDataReg ); + bufAddr += 2; + wordCnt -- ; + } +} + +//************************************************************* + +static void pio_rep_outword( unsigned char addrDataReg, + unsigned char * bufAddr, + long wordCnt ) + +{ + + // Warning: Avoid calling this function with + // wordCnt > 16384 (transfers 32768 bytes). + + //!!! repeat write a 16-bit register (ATA Data register when + //!!! ATA status is BSY=0 DRQ=1). For example: + + while ( wordCnt > 0 ) + { + pio_outword( addrDataReg, * (unsigned int *) bufAddr ); + bufAddr += 2; + wordCnt -- ; + } +} + +//************************************************************* + +static void pio_rep_indword( unsigned char addrDataReg, + unsigned char * bufAddr, + long dwordCnt ) + +{ + + // Warning: Avoid calling this function with + // dwordCnt > 8192 (transfers 32768 bytes). + + //!!! repeat read a 32-bit register (ATA Data register when + //!!! ATA status is BSY=0 DRQ=1). For example: + + while ( dwordCnt > 0 ) + { + * (unsigned long *) bufAddr = pio_indword( addrDataReg ); + bufAddr += 4; + dwordCnt -- ; + } +} + +//************************************************************* + +static void pio_rep_outdword( unsigned char addrDataReg, + unsigned char * bufAddr, + long dwordCnt ) + +{ + + // Warning: Avoid calling this function with + // dwordCnt > 8192 (transfers 32768 bytes). + + //!!! repeat write a 32-bit register (ATA Data register when + //!!! ATA status is BSY=0 DRQ=1). For example: + + while ( dwordCnt > 0 ) + { + pio_outdword( addrDataReg, * (unsigned long *) bufAddr ); + bufAddr += 4; + dwordCnt -- ; + } +} + + +//************************************************************* +// +// Command timing functions +// +//************************************************************** + + +//static long tmr_cmd_start_time; // command start time - see the + // tmr_set_timeout() and + // tmr_chk_timeout() functions. + +//************************************************************* +// +// tmr_set_timeout() - get the command start time +// +//************************************************************** + +static void tmr_set_timeout( void ) + +{ + + // get the command start time + tmr_cmd_start_time = time18(); +} + +//************************************************************* +// +// tmr_chk_timeout() - check for command timeout. +// +// Gives non-zero return if command has timed out. +// +//************************************************************** + +static int tmr_chk_timeout( void ) + +{ + long curTime; + + // get current time + curTime = time18(); + + // timed out yet ? + if ( curTime >= ( tmr_cmd_start_time + + ( TMR_TIME_OUT * SYSTEM_TIMER_TICKS_PER_SECOND ) ) ) + return 1; // yes + + // no timeout yet + return 0; +} + +// end mindrvr.c + \ No newline at end of file Property changes on: branches/prasys/i386/libsaio/mindrvr.c ___________________________________________________________________ Added: svn:executable + * Index: branches/prasys/i386/libsaio/mindrvr.h =================================================================== --- branches/prasys/i386/libsaio/mindrvr.h (revision 0) +++ branches/prasys/i386/libsaio/mindrvr.h (revision 51) @@ -0,0 +1,486 @@ +//******************************************************************** +// MINIMUM ATA LOW LEVEL I/O DRIVER -- MINDRVR.H +// +// by Hale Landis (hlandis@ata-atapi.com) +// +// There is no copyright and there are no restrictions on the use +// of this ATA Low Level I/O Driver code. It is distributed to +// help other programmers understand how the ATA device interface +// works and it is distributed without any warranty. Use this +// code at your own risk. +// +// Minimum ATA Driver (MINDRVR) is a subset of ATADRVR. MINDRVR +// has a single header file and a single C file. MINDRVR can +// be used as the starting point for an ATADRVR for an embedded +// system. NOTE all the places in the MINDRVR.H and MINDRVR.C files +// where there is a comment containing the string "!!!". +// +// Use the header file mindrvr.h in any C files that call MINDRVR +// functions. +// +// This code is based on the ATA/ATAPI-4,-5 and -6 standards and +// on interviews with various ATA controller and drive designers. +// +// Note that MINDRVR does not support ATA CHS addressing. +// +// Most of the MINDRVR code is standard C code and should compile +// using any C compiler. It has been tested using Borland C/C++ 4.5. +// +// This C source file is the header file for the driver +// and is used in the MINDRVR.C files and must also be used +// by any program using the MINDRVR code/functions. +//******************************************************************** + +#define MIN_ATA_DRIVER_VERSION "0H" + +//******************************************************************** +// +// !!! What parts of MINDRVR do you want in your build? +// +//******************************************************************** + +#define INCLUDE_ATA_DMA 1 // not zero to include ATA_DMA + +#define INCLUDE_ATAPI_PIO 1 // not zero to include ATAPI PIO + +#define INCLUDE_ATAPI_DMA 1 // not zero to include ATAPI DMA + +//******************************************************************** +// +// !!! System specific functions and data you must supply +// +//******************************************************************** + +// You must supply a function that waits for an interrupt from the +// ATA controller. This function should return 0 when the interrupt +// is received and a non zero value if the interrupt is not received +// within the time out period. +/* +long tmr_time_out = 20L; + +long tmr_cmd_start_time; +*/ +//extern int SYSTEM_WAIT_INTR_OR_TIMEOUT( void ); + +extern unsigned int time18 ( void ); + +//extern long SYSTEM_READ_TIMER (void); +/* +int tmr_chk_timeout( void ) + +{ + long curTime; + + // get current time + curTime = time18(); + + // if we have passed midnight, restart + if ( curTime < tmr_cmd_start_time ) + { + tmr_cmd_start_time = curTime; + return 0; + } + + // timed out yet ? + if ( curTime >= ( tmr_cmd_start_time + ( tmr_time_out * 18L ) ) ) + return 1; // yes + + // no timeout yet + return 0; +} + +void tmr_set_timeout( void ) + +{ + + // get the command start time + tmr_cmd_start_time = time18(); +} +*/ +// You must supply a function that returns a system timer value. This +// should be a value that increments at some constant rate. + + + +// This defines the number of system timer ticks per second. + +#define SYSTEM_TIMER_TICKS_PER_SECOND 18L + +//******************************************************************** +// +// !!! ATA controller hardware specific data +// +//******************************************************************** + +// ATA Command Block base address +// (the address of the ATA Data register) + +//values is for secondary port +//#define PIO_BASE_ADDR1 ( (unsigned char *) 0x1f0 ) +//#define PIO_BASE_ADDR1 ( (unsigned char *) 0xd600 ) +// ATA Control Block base address +// (the address of the ATA DevCtrl +// and AltStatus registers) +//#define PIO_BASE_ADDR2 ( (unsigned char *) 0x3f6 ) +//#define PIO_BASE_ADDR2 ( (unsigned char *) 0xd700 ) + +// BMIDE base address (address of +// the BMIDE Command register for +// the Primary or Secondary side of +// the PCI ATA controller) +//#define PIO_BMIDE_BASE_ADDR ( (unsigned char *) 0x0000 ) + +// Size of the ATA Data register - allowed values are 8, 16 and 32 +#define PIO_DEFAULT_XFER_WIDTH 16 + +// Interrupts or polling mode - not zero to use interrrupts +// Note: Interrupt mode is required for DMA +#define INT_DEFAULT_INTERRUPT_MODE 0 + +// Command time out in seconds +#define TMR_TIME_OUT 20 + +//************************************************************** +// +// Data that MINDRVR makes available. +// +//************************************************************** + +// public interrupt handler data + +extern unsigned char int_ata_status; // ATA status read by interrupt handler + +extern unsigned char int_bmide_status; // BMIDE status read by interrupt handler + +// Interrupt or Polling mode flag. + +extern unsigned char int_use_intr_flag; // not zero to use interrupts + +// ATA Data register width (8, 16 or 32) + +extern unsigned char pio_xfer_width; + +extern unsigned char pio_inbyte( unsigned char addr ); + +extern void pio_outbyte( int addr, unsigned char data ); + +// Command and extended error information returned by the +// reg_reset(), reg_non_data_*(), reg_pio_data_in_*(), +// reg_pio_data_out_*(), reg_packet() and dma_pci_*() functions. + +struct REG_CMD_INFO +{ + // command code + unsigned char cmd; // command code + // command parameters + unsigned int fr; // feature (8 or 16 bits) + unsigned int sc; // sec cnt (8 or 16 bits) + unsigned int sn; // sec num (8 or 16 bits) + unsigned int cl; // cyl low (8 or 16 bits) + unsigned int ch; // cyl high (8 or 16 bits) + unsigned char dh; // device head + unsigned char dc; // device control + long ns; // actual sector count + int mc; // current multiple block setting + unsigned char lbaSize; // size of LBA used + #define LBACHS 0 // last command used ATA CHS (not supported by MINDRVR) + // -or- last command was ATAPI PACKET command + #define LBA28 28 // last command used ATA 28-bit LBA + #define LBA48 48 // last command used ATA 48-bit LBA + unsigned long lbaLow; // lower 32-bits of ATA LBA + unsigned long lbaHigh; // upper 32-bits of ATA LBA + // status and error regs + unsigned char st; // status reg + unsigned char as; // alt status reg + unsigned char er ; // error reg + // driver error codes + unsigned char ec; // detailed error code + unsigned char to; // not zero if time out error + // additional result info + long totalBytesXfer; // total bytes transfered + long drqPackets; // number of PIO DRQ packets +} ; + +extern struct REG_CMD_INFO reg_cmd_info; + +// Configuration data for device 0 and 1 +// returned by the reg_config() function. + +extern int reg_config_info[2]; + +#define REG_CONFIG_TYPE_NONE 0 +#define REG_CONFIG_TYPE_UNKN 1 +#define REG_CONFIG_TYPE_ATA 2 +#define REG_CONFIG_TYPE_ATAPI 3 + +//************************************************************** +// +// Global defines -- ATA register and register bits. +// command block & control block regs +// +//************************************************************** + +// These are the offsets into pio_reg_addrs[] + +#define CB_DATA 0 // data reg in/out cmd_blk_base1+0 +#define CB_ERR 1 // error in cmd_blk_base1+1 +#define CB_FR 1 // feature reg out cmd_blk_base1+1 +#define CB_SC 2 // sector count in/out cmd_blk_base1+2 +#define CB_SN 3 // sector number in/out cmd_blk_base1+3 +#define CB_CL 4 // cylinder low in/out cmd_blk_base1+4 +#define CB_CH 5 // cylinder high in/out cmd_blk_base1+5 +#define CB_DH 6 // device head in/out cmd_blk_base1+6 +#define CB_STAT 7 // primary status in cmd_blk_base1+7 +#define CB_CMD 7 // command out cmd_blk_base1+7 +#define CB_ASTAT 8 // alternate status in ctrl_blk_base2+6 +#define CB_DC 8 // device control out ctrl_blk_base2+6 + +// error reg (CB_ERR) bits + +#define CB_ER_ICRC 0x80 // ATA Ultra DMA bad CRC +#define CB_ER_BBK 0x80 // ATA bad block +#define CB_ER_UNC 0x40 // ATA uncorrected error +#define CB_ER_MC 0x20 // ATA media change +#define CB_ER_IDNF 0x10 // ATA id not found +#define CB_ER_MCR 0x08 // ATA media change request +#define CB_ER_ABRT 0x04 // ATA command aborted +#define CB_ER_NTK0 0x02 // ATA track 0 not found +#define CB_ER_NDAM 0x01 // ATA address mark not found + +#define CB_ER_P_SNSKEY 0xf0 // ATAPI sense key (mask) +#define CB_ER_P_MCR 0x08 // ATAPI Media Change Request +#define CB_ER_P_ABRT 0x04 // ATAPI command abort +#define CB_ER_P_EOM 0x02 // ATAPI End of Media +#define CB_ER_P_ILI 0x01 // ATAPI Illegal Length Indication + +// ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC) + +#define CB_SC_P_TAG 0xf8 // ATAPI tag (mask) +#define CB_SC_P_REL 0x04 // ATAPI release +#define CB_SC_P_IO 0x02 // ATAPI I/O +#define CB_SC_P_CD 0x01 // ATAPI C/D + +// bits 7-4 of the device/head (CB_DH) reg + +#define CB_DH_LBA 0x40 // LBA bit +#define CB_DH_DEV0 0x00 // select device 0 +#define CB_DH_DEV1 0x10 // select device 1 +// #define CB_DH_DEV0 0xa0 // select device 0 (old definition) +// #define CB_DH_DEV1 0xb0 // select device 1 (old definition) + +// status reg (CB_STAT and CB_ASTAT) bits + +#define CB_STAT_BSY 0x80 // busy +#define CB_STAT_RDY 0x40 // ready +#define CB_STAT_DF 0x20 // device fault +#define CB_STAT_WFT 0x20 // write fault (old name) +#define CB_STAT_SKC 0x10 // seek complete (only SEEK command) +#define CB_STAT_SERV 0x10 // service (overlap/queued commands) +#define CB_STAT_DRQ 0x08 // data request +#define CB_STAT_CORR 0x04 // corrected (obsolete) +#define CB_STAT_IDX 0x02 // index (obsolete) +#define CB_STAT_ERR 0x01 // error (ATA) +#define CB_STAT_CHK 0x01 // check (ATAPI) + +// device control reg (CB_DC) bits + +#define CB_DC_HOB 0x80 // High Order Byte (48-bit LBA) +// #define CB_DC_HD15 0x00 // bit 3 is reserved +// #define CB_DC_HD15 0x08 // (old definition of bit 3) +#define CB_DC_SRST 0x04 // soft reset +#define CB_DC_NIEN 0x02 // disable interrupts + +//************************************************************** +// +// Most mandtory and optional ATA commands +// +//************************************************************** + +#define CMD_CFA_ERASE_SECTORS 0xC0 +#define CMD_CFA_REQUEST_EXT_ERR_CODE 0x03 +#define CMD_CFA_TRANSLATE_SECTOR 0x87 +#define CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD +#define CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38 +#define CMD_CHECK_POWER_MODE1 0xE5 +#define CMD_CHECK_POWER_MODE2 0x98 +#define CMD_DEVICE_RESET 0x08 +#define CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90 +#define CMD_FLUSH_CACHE 0xE7 +#define CMD_FLUSH_CACHE_EXT 0xEA +#define CMD_FORMAT_TRACK 0x50 +#define CMD_IDENTIFY_DEVICE 0xEC +#define CMD_IDENTIFY_DEVICE_PACKET 0xA1 +#define CMD_IDENTIFY_PACKET_DEVICE 0xA1 +#define CMD_IDLE1 0xE3 +#define CMD_IDLE2 0x97 +#define CMD_IDLE_IMMEDIATE1 0xE1 +#define CMD_IDLE_IMMEDIATE2 0x95 +#define CMD_INITIALIZE_DRIVE_PARAMETERS 0x91 +#define CMD_INITIALIZE_DEVICE_PARAMETERS 0x91 +#define CMD_NOP 0x00 +#define CMD_PACKET 0xA0 +#define CMD_READ_BUFFER 0xE4 +#define CMD_READ_DMA 0xC8 +#define CMD_READ_DMA_EXT 0x25 +#define CMD_READ_DMA_QUEUED 0xC7 +#define CMD_READ_DMA_QUEUED_EXT 0x26 +#define CMD_READ_MULTIPLE 0xC4 +#define CMD_READ_MULTIPLE_EXT 0x29 +#define CMD_READ_SECTORS 0x20 +#define CMD_READ_SECTORS_EXT 0x24 +#define CMD_READ_VERIFY_SECTORS 0x40 +#define CMD_READ_VERIFY_SECTORS_EXT 0x42 +#define CMD_RECALIBRATE 0x10 +#define CMD_SEEK 0x70 +#define CMD_SET_FEATURES 0xEF +#define CMD_SET_MULTIPLE_MODE 0xC6 +#define CMD_SLEEP1 0xE6 +#define CMD_SLEEP2 0x99 +#define CMD_SMART 0xB0 +#define CMD_STANDBY1 0xE2 +#define CMD_STANDBY2 0x96 +#define CMD_STANDBY_IMMEDIATE1 0xE0 +#define CMD_STANDBY_IMMEDIATE2 0x94 +#define CMD_WRITE_BUFFER 0xE8 +#define CMD_WRITE_DMA 0xCA +#define CMD_WRITE_DMA_EXT 0x35 +#define CMD_WRITE_DMA_QUEUED 0xCC +#define CMD_WRITE_DMA_QUEUED_EXT 0x36 +#define CMD_WRITE_MULTIPLE 0xC5 +#define CMD_WRITE_MULTIPLE_EXT 0x39 +#define CMD_WRITE_SECTORS 0x30 +#define CMD_WRITE_SECTORS_EXT 0x34 +#define CMD_WRITE_VERIFY 0x3C + +//************************************************************** +// +// ATA and ATAPI PIO support functions +// +//************************************************************** + +// config and reset funcitons + +extern void pio_set_iobase_addr( unsigned int base1, + unsigned int base2, + unsigned int base3 ); + +extern int reg_config( void ); + +extern int reg_reset( unsigned char devRtrn ); + +// ATA Non-Data command funnctions (for LBA28 and LBA48) + +extern int reg_non_data_lba28( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lba ); + +extern int reg_non_data_lba48( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lbahi, unsigned long lbalo ); + +// ATA PIO Data In command functions (for LBA28 and LBA48) + +extern int reg_pio_data_in_lba28( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lba, + unsigned char * bufAddr, + long numSect, int multiCnt ); + +extern int reg_pio_data_in_lba48( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lbahi, unsigned long lbalo, + unsigned char * bufAddr, + long numSect, int multiCnt ); + +// ATA PIO Data Out command functions (for LBA28 and LBA48) + +extern int reg_pio_data_out_lba28( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lba, + unsigned char * bufAddr, + long numSect, int multiCnt ); + +extern int reg_pio_data_out_lba48( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lbahi, unsigned long lbalo, + unsigned char * bufAddr, + long numSect, int multiCnt ); + +#if INCLUDE_ATAPI_PIO + +// ATAPI Packet PIO function + +extern int reg_packet( unsigned char dev, + unsigned int cpbc, + unsigned char * cdbBufAddr, + int dir, + long dpbc, + unsigned char * dataBufAddr ); + +#endif // INCLUDE_ATAPI_PIO + +//************************************************************** +// +// ATA and ATAPI DMA support functions +// +//************************************************************** + +#if INCLUDE_ATA_DMA || INCLUDE_ATAPI_DMA + +// BMIDE registers and bits + +#define BM_COMMAND_REG 0 // offset to BM command reg +#define BM_CR_MASK_READ 0x00 // read from memory +#define BM_CR_MASK_WRITE 0x08 // write to memory +#define BM_CR_MASK_START 0x01 // start transfer +#define BM_CR_MASK_STOP 0x00 // stop transfer + +#define BM_STATUS_REG 2 // offset to BM status reg +#define BM_SR_MASK_SIMPLEX 0x80 // simplex only +#define BM_SR_MASK_DRV1 0x40 // drive 1 can do dma +#define BM_SR_MASK_DRV0 0x20 // drive 0 can do dma +#define BM_SR_MASK_INT 0x04 // INTRQ signal asserted +#define BM_SR_MASK_ERR 0x02 // error +#define BM_SR_MASK_ACT 0x01 // active + +#define BM_PRD_ADDR_LOW 4 // offset to BM prd addr reg low 16 bits +#define BM_PRD_ADDR_HIGH 6 // offset to BM prd addr reg high 16 bits + +// PCI DMA setup function (usually called once). + +// !!! You may not need this function in your system - see the comments +// !!! for this function in MINDRVR.C. + +extern int dma_pci_config( void ); + +// ATA DMA functions + +extern int dma_pci_lba28( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lba, + unsigned char * bufAddr, + long numSect ); + +extern int dma_pci_lba48( unsigned char dev, unsigned char cmd, + unsigned int fr, unsigned int sc, + unsigned long lbahi, unsigned long lbalo, + unsigned char * bufAddr, + long numSect ); + +#endif // INCLUDE_ATA_DMA or INCLUDE_ATAPI_DMA + +#if INCLUDE_ATAPI_DMA + +// ATA DMA function + +extern int dma_pci_packet( unsigned char dev, + unsigned int cpbc, + unsigned char * cdbBufAddr, + int dir, + long dpbc, + unsigned char * dataBufAddr ); + +#endif // INCLUDE_ATAPI_DMA + +// end mindrvr.h + \ No newline at end of file Property changes on: branches/prasys/i386/libsaio/mindrvr.h ___________________________________________________________________ Added: svn:executable + *