Back
//main.htm Project: main source file created 10/11/09 19:21:23

//Nov 2009 new IDE driver, now separate from ChaOS OS image

//supports 32-bit LBA addressing (2Tb). not difficult to modify
//for full 48-bit functionality (bits 47-32 presently forced to zero)
//this module could be shortened by moving sections of duplicated code
//to subfunctions; but this increases the risk that one small coding error
//causes IDE support to fail for the whole system. UDMA and non-UDMA functions
//for read and write are used, according to the setting of the IDE bmiba+2
//register (bits 0x40 for slave, 0x20 for master); these bits are usually
//set by BIOS to indicate that a drive is UDMA capable and ready

//should a problem arise whilst recoding UDMA functions, outp(bmiba+2,0) will
//cause the driver to revert to PIO, enabling fall-back disk support for
//recompilation of this module. vice versa to force UDMA on, if a PIO function
//is dodgy. If neither is possible, revert to BIOS boot drive, which provided
//read-write disk support by real mode callbacks to int 0x13
//(the method used to develop this driver!)

#pragma breakpoints on
#include {conio.htm}
#include {string.htm}
#include {stdlib.htm}
#include {time.htm}
#include {accdis.htm}

#include {pci.htm}
#include {dev.htm}
#include {drive.htm}

SL  main(UL argc,CH* argv[])
{
    return 0;
}
SL  term(VD)
{
    return 0;
}
struct ATD
{
    HDG hdg;
    UL  id,port,regport,bmiba;
    UL  udma,lba48,sts;
    DEV* ifc;
    ATA ata;
};
struct ATC
{
    UL  port,regport,bmiba;
    UC* udmabuf;
    UC* udmaprd;
    DEV* master;
    DEV* slave;
};

VD  poll(VD);

SL  readlba(ATD* a,UL lba,UL sectors,UC* buf)
{
    if(!a->lba48){lba&=0x0fffffff;}     //max 128Gb
//NOTE call this function with sectors>128 you will only read 128 sectors
    outp(a->regport,0); //NIEN=0;
    if(sectors>128){sectors=128;}
//setup for ata command
    if(a->lba48){outp(a->port+6,0xe0|a->id);}
    else{outp(a->port+6,0xe0|(lba>>24)|a->id);}//lba|dev0|LBA 27:24

    if(a->lba48){outp(a->port+2,0);}
    outp(a->port+2,sectors);//sector count //outp(port+2,0) = 256 sectors
    if(a->lba48){outp(a->port+3,lba>>24);}
    outp(a->port+3,lba);      //LBA  7:0
    if(a->lba48){outp(a->port+4,0);}
    outp(a->port+4,lba>>8);   //LBA 15:8
    if(a->lba48){outp(a->port+5,0);}
    outp(a->port+5,lba>>16);  //LBA 23:16
    if(a->lba48){outp(a->port+7,0x24);}   //read ext
    else        {outp(a->port+7,0x20);}   //read

//if no DRQ (data waiting for us) after 1 second we exit, no attempt to recover
    if(!mseciowait(a->regport,3,1,1000)){return 1;}

    UL  n;
    UL  dwords=0x200/4;        //dwords per sector
    UC* ptr=buf;
    UL  ataport0=a->port;
    for(n=0;nregport,7,0,1000)){return 1;}
         ptr+=0x200;
        }

//and if the disk doesn't immediately show READY and SEEKCOMPLETE
//it's probably a duffer too
    if(inp(a->regport)!=0x50){return 1;}

    return a->sts&1;
}
SL  readudma(ATD* a,UL lba,UL sectors,UC* buf)
{
    if(!a->lba48){lba&=0x0fffffff;}     //max 128Gb
    outp(a->regport,0); //NIEN=0;
    UL n=0;
    UL retry=0;
    UL  port   =a->port;
    UL  ctlport=a->regport;

    ATC* atc=(ATC*)a->ifc->usr;
    UL* prd    =(UL*)atc->udmaprd;
    UC* udmabuf=     atc->udmabuf;
    UL  oldprd=inpd(a->bmiba+4);

RETRY:
//prepare prd table
    *prd=(UL)udmabuf;
    *(prd+1)=sectors*512;
    if(*(prd+1)>0x10000)
        {
//         clidisplay("UDMA transfer>64k");
         *(prd+3)=*(prd+1)-0x10000;
         *(prd+3)|=0x80000000;
         *(prd+1)=0x10000;
         *(prd+2)=(UL)(udmabuf+0x10000);
         }
    else
        {
         *(prd+1)|=0x80000000;
        }

//load the prd table register
    outpd(a->bmiba+4,(UL)prd);

//clear interrupt and error bits in status register
    UL  p=inp(a->bmiba+2)&0x60;  //preserve UDMA capable bits
    outp(a->bmiba+2,p|6);

//clear the bus master
    outp(a->bmiba,8);

//clear INTR occurred flag
    a->sts=0;

//start the device on a DMA command
    if(a->lba48){outp(port+6,0xe0|a->id);}
    else        {outp(port+6,0xe0|(lba>>24)|a->id);}//lba|dev0|LBA 27:24

    if(a->lba48){outp(port+2,0);}
    outp(port+2,sectors);//sector count //outp(port+2,0) = 256 sectors
    if(a->lba48){outp(port+3,lba>>24);}
    outp(port+3,lba);      //LBA  7:0
    if(a->lba48){outp(port+4,0);}
    outp(port+4,lba>>8);   //LBA 15:8
    if(a->lba48){outp(port+5,0);}
    outp(port+5,lba>>16);  //LBA 23:16
    if(a->lba48){outp(port+7,0x25);}   //read dma ext
    else        {outp(port+7,0xc8);}   //read dma

//start the bus master
    outp(a->bmiba,9);

    UL  t=uptime;
    while(t+50>uptime){if(a->sts){break;}}
    if(!a->sts||(a->sts&1))
       {
//        displayat(99,0,"read UDMA waitint error drive %1c controller %1c",&h->id,&h->hdc->id);
        outp(a->bmiba,8);
        outp(a->bmiba+2,p|6);
        goto UDMAERROR;
       }

    n=inp(a->bmiba+2)&~0x60;
//stop the bus master
    outp(a->bmiba,8);

    if(n){goto UDMAERROR;}

    n=inp(port+1);     //remember to read the controller status
    if(n){goto UDMAERROR;}

//get target data from DMA buffer
    memcpy(buf,udmabuf,sectors*512);

//clear interrupt and error bits in status register
    outp(a->bmiba+2,p|6);
    outpd(a->bmiba+4,(UL)oldprd);
    return 0;
UDMAERROR:
    outp(a->bmiba+2,p|6);
    outpd(a->bmiba+4,(UL)oldprd);
    a->udma=0;
    return readlba(a,lba,sectors,buf);
}
SL  writelba(ATD* a,UL lba,UL sectors,UC* buf)
{
    if(!a->lba48){lba&=0x0fffffff;}     //max 128Gb
//NOTE call this function with sectors>128 you will only write 128 sectors!
    outp(a->regport,0); //NIEN=0;
    if(sectors>128){sectors=128;}
//setup for ata command
    if(a->lba48){outp(a->port+6,0xe0|a->id);}
    else{outp(a->port+6,0xe0|(lba>>24)|a->id);}//lba|dev0|LBA 27:24

    if(a->lba48){outp(a->port+2,0);}
    outp(a->port+2,sectors);//sector count //outp(port+2,0) = 256 sectors
    if(a->lba48){outp(a->port+3,lba>>24);}
    outp(a->port+3,lba);      //LBA  7:0
    if(a->lba48){outp(a->port+4,0);}
    outp(a->port+4,lba>>8);   //LBA 15:8
    if(a->lba48){outp(a->port+5,0);}
    outp(a->port+5,lba>>16);  //LBA 23:16
    if(a->lba48){outp(a->port+7,0x34);}   //write ext
    else        {outp(a->port+7,0x30);}   //write

//if no DRQ (data waiting for us) after 1 second we exit, no attempt to recover
    if(!mseciowait(a->regport,3,1,1000)){return 1;}

    UL  n;
    UL  dwords=0x200/4;        //dwords per sector
    UC* ptr=buf;
    UL  ataport0=a->port;
    for(n=0;nregport,7,0,1000)){return 1;}
         ptr+=0x200;
        }

//and if the disk doesn't immediately show READY and SEEKCOMPLETE
//it's probably a duffer too
    if(inp(a->regport)!=0x50){return 1;}

    return a->sts&1;
}
SL  writeudma(ATD* a,UL lba,UL sectors,UC* buf)
{
    if(!a->lba48){lba&=0x0fffffff;}     //max 128Gb
    outp(a->regport,0); //NIEN=0;
    UL n=0;
    UL retry=0;
    UL  port   =a->port;
    UL  ctlport=a->regport;

    ATC* atc=(ATC*)a->ifc->usr;
    UL* prd    =(UL*)atc->udmaprd;
    UC* udmabuf=     atc->udmabuf;
    UL  oldprd=inpd(a->bmiba+4);

//move target data to DMA buffer
    memcpy(udmabuf,buf,sectors*512);

RETRY:
//prepare prd table
    *prd=(UL)udmabuf;
    *(prd+1)=sectors*512;
    if(*(prd+1)>0x10000)
        {
//         clidisplay("UDMA transfer>64k");
         *(prd+3)=*(prd+1)-0x10000;
         *(prd+3)|=0x80000000;
         *(prd+1)=0x10000;
         *(prd+2)=(UL)(udmabuf+0x10000);
         }
    else
        {
         *(prd+1)|=0x80000000;
        }

//load the prd table register
    outpd(a->bmiba+4,(UL)prd);

//clear interrupt and error bits in status register
    UL  p=inp(a->bmiba+2)&0x60;  //preserve UDMA capable bits
    outp(a->bmiba+2,p|6);

//clear the bus master
    outp(a->bmiba,8);

//clear INTR occurred flag
    a->sts=0;

//start the device on a DMA command
    if(a->lba48){outp(a->port+6,0xe0|a->id);}
    else{outp(a->port+6,0xe0|(lba>>24)|a->id);}//lba|dev0|LBA 27:24

    if(a->lba48){outp(a->port+2,0);}
    outp(a->port+2,sectors);//sector count //outp(port+2,0) = 256 sectors
    if(a->lba48){outp(a->port+3,lba>>24);}
    outp(a->port+3,lba);      //LBA  7:0
    if(a->lba48){outp(a->port+4,0);}
    outp(a->port+4,lba>>8);   //LBA 15:8
    if(a->lba48){outp(a->port+5,0);}
    outp(a->port+5,lba>>16);  //LBA 23:16
    if(a->lba48){outp(a->port+7,0x35);}   //write dma ext
    else        {outp(a->port+7,0xca);}   //write dma

//start the bus master
    outp(a->bmiba,1);

    UL  t=uptime;
    while(t+50>uptime){if(a->sts){break;}}
    if(!a->sts||(a->sts&1))
       {
//        displayat(99,0,"write UDMA waitint error drive %1c controller %1c",&h->id,&h->hdc->id);
        outp(a->bmiba,8);
        outp(a->bmiba+2,p|6);
        goto UDMAERROR;
       }

    n=inp(a->bmiba+2)&~0x60;
//stop the bus master
    outp(a->bmiba,0);

    if(n){goto UDMAERROR;}

    n=inp(port+1);     //remember to read the controller status
    if(n){goto UDMAERROR;}

//clear interrupt and error bits in status register
    outp(a->bmiba+2,p|6);
    outpd(a->bmiba+4,(UL)oldprd);
    return 0;
UDMAERROR:
    outpd(a->bmiba+4,(UL)oldprd);
    outp(a->bmiba+2,p|6);
    a->udma=0;
    return writelba(a,lba,sectors,buf);
}
SL  read(ATD* a,UL lba,UL sectors,UC* buf)
{
    if(a->udma){return readudma(a,lba,sectors,buf);}
    return readlba(a,lba,sectors,buf);
}
SL  write(ATD* a,UL lba,UL sectors,UC* buf)
{
    if(a->udma){return writeudma(a,lba,sectors,buf);}
    return writelba(a,lba,sectors,buf);
}
SL  logHD(DEV* d);
DEV* matchdrivestring(CH* str,UL verbose);
VD  clidisplaysector(UC* buf);
SL  dsec(UL argc,CH* argv[])
{
    if(argc<2){clidisplay("Usage:DSEC ");return 1;}

    DEV* d=matchdrivestring(argv[1],1);
    if(!d){return 1;}

    UC  tmp[2048];

    if(!read(d,0,1,tmp))
        {
         clidisplaysector(tmp);
        }



    return 0;
}

SL  atdsvc(DEV* d,UL service)
{
    ATD* a=(ATD*)d->usr;
    ATC* c=(ATC*)d->ifc->usr;
    UL  blk,lba,sectors;
    UL  p,mb;
    UC* ptr=NULL;

switch(service)
    {//Mandatory message support//////////////////
     case DM_IDENT:
        return 0;
     case DM_TRIGGER:
        return 0;
     case DM_HANDLER:
        a->sts=inp(a->regport); //clear legacy interrupt
//        catdisp(5,70,"%s irq",d->name);
        p=inp(a->bmiba+2);
        outp(a->bmiba+2,p|4);   //clear bus master interrupt
        return 0;
     case DM_PREINIT:   //setups before interrupts are running
#pragma breakpoints on
        d->usr=calloc(sizeof(ATD),1);
        if(!d->usr){return 1;}
        a=(ATD*)d->usr;
        memcpy(a->ata,c->udmabuf,512);
        a->ifc=d->ifc;
        ATA*aa=&a->ata;
        a->hdg.c=a->ata.cyls;
        a->hdg.h=a->ata.heads;
        a->hdg.s=a->ata.spt;
        a->hdg.bps=512;
        if(a->ata.cmdset1&0x400)
            {
             a->hdg.lbs  =a->ata.lba48;
             a->hdg.lbshi=a->ata.lba48hi;
             a->lba48=1;
            }
        else
            {
             a->hdg.lbs=a->ata.lbasectors;
            }
        a->hdg.cap=a->hdg.lbs/0x800;
        a->id=d->init0<<4;
        a->port   =port0(d->ifc);
        a->regport=port1(d->ifc)+2;
        a->bmiba  =port2(d->ifc);
        a->udma=inp(a->bmiba+2)&(0x20<init0);
        if(!a->udma)
            {
             if(a->ata.udmaset)
                {//drive has been set to udma by BIOS,
                 //so update bmiba+2
                 p=inp(a->bmiba+2);
                 p|=(0x20<init0);
                 outp(a->bmiba+2,p);
                 a->udma=inp(a->bmiba+2)&(0x20<init0);
                }
            }
        if(!read(a,0,1,(UC*)&a->hdg.pt))
            {
             logHD(d);
             return 0;
            }
        return 0;
     case DM_INIT:
        d->flg|=DF_COMMANDS;
        return 0;
     case DM_TERM:
        return 0;
     case DM_RESET:
        return 0;
     case DM_POLL:
        return 0;
     case DM_MODELNAME:
        strncpy((CH*)d->svbuf,(CH*)a->ata.model,20);
        return 0;
     case DM_CLIDISPINFO:
        CH  envstr[80];envstr[0]=0;
        CH* ptr1;if(d->init0){ptr1="Slave";}else{ptr1="Master";};
//        mb=a->hdg.lbs/2048;
        catprint(envstr,"%s %s (%7lMb) %s",d->ifc->name,ptr1,&a->hdg.cap,a->ata.model);
        clidisplay(envstr);
        return 0;
     case DM_READ:
        lba=d->sv0;
        sectors=d->sv1;
        ptr=d->svbuf;
        blk=0x80;
        while(sectors)
            {
             if(blk>sectors){blk=sectors;}
             if(read(a,lba,blk,ptr)){return 1;}
             ptr+=a->hdg.bps*blk;
             lba+=blk;
             sectors-=blk;
            }
        return 0;
     case DM_WRITE:
        lba=d->sv0;
        sectors=d->sv1;
        ptr=d->svbuf;
        blk=0x80;
        while(sectors)
            {
             if(blk>sectors){blk=sectors;}
             if(write(a,lba,blk,ptr)){return 1;}
             ptr+=a->hdg.bps*blk;
             lba+=blk;
             sectors-=blk;
            }
        return 0;
     case DM_GETCOMMAND:
        if(!stricmp((CH*)d->svbuf,"dsec")){d->svbuf=(UC*)dsec;return 0;}
        return 1;
     default:return 0;
    }
}

VD*  topmal(UL size);

VD  allocudmabuf(ATC* a)
{//64k aligned controller workspace
    if(!a->udmabuf){a->udmabuf=topmal(0x10000);}//64k, max 128 sectors
    if(!a->udmaprd){a->udmaprd=topmal(0x100);}
}
VD  swab(UC* buf,UL count)
{
    UI* w=(UI*)buf;
    while(count--)
        {
         *w=*(Um*)w;
         w++;
        }
}

SL  identify(ATC* a,UL id,UL cmd)
{
    outp(a->port+6,0xa0+(id<<4));

    outp(a->port+7,cmd);
    if(!mseciowait(a->regport,7,0,1000)){return 1;}
    if(!(inp(a->regport)&0x08))        {return 1;}

    UI* iptr=(UI*)a->udmabuf;
    UL  ctr=0;
    while(inp(a->regport)&0x08)
        {
         *iptr++=inpw(a->port);
         ctr++;
        }
    if(ctr!=0x100){brk return 1;}
    swab(a->udmabuf+20,10);    //unswizzle serial number
    swab(a->udmabuf+46,24);    //unswizzle firmware revision and model

    return 0;
}
SL  atcsvc  (DEV* d,UL service)
{
//ATC device handler
    ATC* a=(ATC*)d->usr;
    UL  p;
switch(service)
    {//Mandatory message support//////////////////
     case DM_IDENT:
        return 0;   //drv was matched by vm
     case DM_TRIGGER:
        return inp(a->bmiba+2)&4;
     case DM_HANDLER:
        UL sts=inp(a->port+7);      //clear legacy interrupt
        UL w=inp(a->port+6);
        DEV* sub=NULL;
        if(w&0x10){sub=a->slave;}
        else      {sub=a->master;}
        if(sub&&sub->drv&&sub->drv->svc)
            {
             return devsvc(sub,DM_HANDLER);
            }
        else
            {
             UL p=inp(a->bmiba+2);
             outp(a->bmiba+2,p|4);  //clear bus master interrupt
            }
        return 0;
     case DM_PREINIT:
        a=calloc(1,sizeof(ATC));
        if(!a){return 1;}
        d->usr=a;
        //parent device should have created resource records for
        //ata ports on this device:
        a->port   =port0(d);
        a->regport=port1(d)+2;
        a->bmiba  =port2(d);
//        *d->name='q';       //prevent legacy ATC from kicking in
        return 0;
     case DM_INIT:
        outp(a->port+6,0xa0);
        p=inp(a->regport)&0xd8;
        if((p==0x50)||(p==0x00))
            {//master is ready
             allocudmabuf(a);
             if(!identify(a,0,0xec))
                {
                 a->master=createdev( 4,0x01010000,0,"pda",d->init0*2,4,d);
                 if(a->master){initdev(a->master,0,0);}
                }
             else if(!identify(a,0,0xa1))
                {
                 a->master=createdev( 5,0x01010000,0,"qda",d->init0*2,4,d);
                 if(a->master){initdev(a->master,0,0);}
                }
            }
        outp(a->port+6,0xb0);
        p=inp(a->regport)&0xd8;
        if((p==0x50)||(p==0x00))
            {//slave is ready
             allocudmabuf(a);
             if(!identify(a,1,0xec))
                {
                 a->slave=createdev( 4,0x01010000,0,"pda",1+d->init0*2,4,d);
                 if(a->slave){initdev(a->slave,1,0);}
                }
             else if(!identify(a,1,0xa1))
                {
                 a->slave=createdev( 5,0x01010000,0,"qda",1+d->init0*2,4,d);
                 if(a->slave){initdev(a->slave,1,0);}
                }
            }
        outp(a->port+6,0xa0);
        return 0;
     case DM_TERM:
        return 0;
     case DM_CHILDHANDLER:
        return 0;
     case DM_CLIDISPINFO:
        return 0;
    }
}
UL*  resrec(DEV* d,UL type,UL count);
SL  idesvc  (DEV* d,UL service)
{
//IDE device handler

    UL  n,ck,ck1,ck2;
    UL* lptr;
switch(service)
    {//Mandatory message support//////////////////
     case DM_IDENT:
        return 0;   //drv was matched by vm
     case DM_TRIGGER:
        return 0;
     case DM_HANDLER:
        return 0;
     case DM_PREINIT:
        //!NORMALLY!, createpcidevice will have allocated
        //the ata resources is this order
        //port0()   ata0 port
        //port1()   ata0 regport
        //port2()   ata1 port
        //port3()   ata1 regport
        //port4()   ata0 bmiba
        //port4()+8 ata1 bmiba
        //port5()   ata extension (sata dta/idx) pair
        //!BUT!.. support old hardware on an as-need basis !!
        //....... if old PCI type found, delete resources, then
        //....... re-allocate, so port0()->port(4) are correct
        if(d->pc)
            {
             brk return 1;
            }
        if(d->pci&&(d->pci->type==0x01018005))
            {
             //special case for Intel 845 mainboard
             free(d->res);d->res=NULL;
             getres(d,rtIRQ,0x10e,0);
             getres(d,rtIRQ,0x10f,0);
             getres(d,rtIO ,0x1f0,8);           //ata0 port
             getres(d,rtIO ,0x3f4,4);           //ata0 regport
             getres(d,rtIO ,0x170,8);           //ata1 port
             getres(d,rtIO ,0x374,4);           //ata1 regport
             n=readPCIdword(d->pci,0x20)&~1;
             getres(d,rtIO ,n    ,0x10);        //ata0/1 bmiba
             return 0;
            }
        else if(d->pci&&(d->pci->type==0x01018001))
            {
             //special case for Intel 440 mainboard
             free(d->res);d->res=NULL;
             getres(d,rtIRQ,0x10e,0);
             getres(d,rtIRQ,0x10f,0);
             getres(d,rtIO ,0x1f0,8);           //ata0 port
             getres(d,rtIO ,0x3f4,4);           //ata0 regport
             getres(d,rtIO ,0x170,8);           //ata1 port
             getres(d,rtIO ,0x374,4);           //ata1 regport
             n=readPCIdword(d->pci,0x20)&~1;
             getres(d,rtIO ,n    ,0x10);        //ata0/1 bmiba
             return 0;
            }
        //alter default resource lengths
        lptr=resrec(d,rtIO,0);lptr[1]=8;
        lptr=resrec(d,rtIO,1);lptr[1]=4;
        lptr=resrec(d,rtIO,2);lptr[1]=8;
        lptr=resrec(d,rtIO,3);lptr[1]=4;
        lptr=resrec(d,rtIO,4);lptr[1]=0x10;
        return 0;
     case DM_INIT:
        //create primary and secondary ata devices
        //and transfer port resources to them
        DEV* pri;
        DEV* sec;
        UL  idetim=readPCIdword(d->pci,0x40);
        UL  udrvs=inp(port4(d)+0x02)&0x60;
        //idetim&0x80008000 shows IDE enabled
        if((idetim&0x00008000)||udrvs)
            {//create Primary IDE
             pri=createdev(3,0x01fd0000,0,d);
             if(pri)
             {
             //transfer port resources
             lptr=resrec(d,rtIO ,0);if(lptr){getres(pri,rtIO ,*lptr,*(lptr+1));}
             lptr=resrec(d,rtIO ,1);if(lptr){getres(pri,rtIO ,*lptr,*(lptr+1));}
             lptr=resrec(d,rtIO ,4);if(lptr){lptr[1]=8;getres(pri,rtIO ,*lptr,8);}
             lptr=resrec(d,rtIRQ,0);
             if(lptr){releaseIRQhandler(d);getres(pri,rtIRQ,*lptr,*(lptr+1));}
             pri->init0=0;
             initdev(pri);
             }
            }
        udrvs=inp(port4(d)+0x0a)&0x60;
        if((idetim&0x80000000)||udrvs)
            {//create Secondary IDE
             sec=createdev(3,0x01fd0000,0,d);
             if(sec)
             {
             //transfer port resources
             lptr=resrec(d,rtIO ,2);if(lptr){getres(sec,rtIO ,*lptr,  *(lptr+1));}
             lptr=resrec(d,rtIO ,3);if(lptr){getres(sec,rtIO ,*lptr,  *(lptr+1));}
             lptr=resrec(d,rtIO ,4);if(lptr){lptr[1]=8;getres(sec,rtIO ,8+*lptr,8);}
             lptr=resrec(d,rtIRQ,1);
             if(lptr){releaseIRQhandler(d);getres(sec,rtIRQ,*lptr,*(lptr+1));}
             else    {lptr=resrec(d,rtIRQ,0);
                      if(lptr){releaseIRQhandler(d);getres(sec,rtIRQ,*lptr,*(lptr+1));}}
             sec->init0=1;
             initdev(sec);
             }
            }
        return 0;
     case DM_TERM:
        return 0;
     case DM_CHILDHANDLER:
        return 0;
     case DM_CLIDISPINFO:
        return 0;
    }
}

    DRV idedrv0={ 0,0x01018a00,0x01018000,idesvc,"IDE 8a00"};
    DRV idedrv1={ 0,0x01018500,0x01018000,idesvc,"IDE 8500"};
    DRV idedrv2={ 0,0x01018503,0x01018000,idesvc,"IDE 8503"};
    DRV idedrv3={ 0,0x01018a01,0x01018000,idesvc,"IDE 8a01"};
    DRV idedrv4={ 0,0x01018f01,0x01018000,idesvc,"IDE 8f01"};
    DRV idedrv5={ 0,0x01018f00,0x01018000,idesvc,"IDE 8f00"};
    DRV idedrv6={ 0,0x01018003,0x01018000,idesvc,"IDE 8003"};
    DRV idedrv7={ 0,0x01018005,0x01018000,idesvc,"IDE 8005"};
    DRV idedrv8={ 0,0x01018501,0x01018000,idesvc,"IDE 8501"};
    DRV idedrv9={ 0,0x01018001,0x01018000,idesvc,"IDE 8001"};
    DRV atcdrv={ 3,0x01fd0000,0x01fd0000,atcsvc,"ATA"};
    DRV atddrv={ 4,0x01010000,0x01010000,atdsvc,"ATA disk"};