Software Reset on all ATA Drives present on the Bus.

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
shikhin
Member
Member
Posts: 274
Joined: Sat Oct 09, 2010 3:35 am
Libera.chat IRC: shikhin
Contact:

Software Reset on all ATA Drives present on the Bus.

Post by shikhin »

Hello,
I am implementing, ATA Driver for my Kernel. Previously I tried doing so by using a tutorial. However, after some kind suggestions from few people I decided to write my own Driver. AFAIK, a software reset can be done by setting the second bit on the Device Controller Register. So I was having a problem while trying to do a software reset on the ATA drives. The problem was that the BSY bit wasnt clearing even after the Software Reset. Here is the code to do the software reset:

Code: Select all

   
ide_write(0, ATA_REG_CONTROL, 0x02);
ide_write(1, ATA_REG_CONTROL, 0x02);
u8int status = ide_read(i, ATA_REG_STATUS);
if(!(status & ATA_SR_BSY))
	kputs("Done\n");
While testing the Done wasnt printed. Why isnt the Software reset hapenning? If it is happening, why is the BSY not clearing?
http://shikhin.in/

Current status: Gandr.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Software Reset on all ATA Drives present on the Bus.

Post by gerryg400 »

Reset is bit 2, but bit 2 is the third bit. You should write 0x4.
If a trainstation is where trains stop, what is a workstation ?
shikhin
Member
Member
Posts: 274
Joined: Sat Oct 09, 2010 3:35 am
Libera.chat IRC: shikhin
Contact:

Re: Software Reset on all ATA Drives present on the Bus.

Post by shikhin »

Hi,
Oh. I thought that the second bit, was bit 2. :oops: Well I fixed the error, but it was quickly followed with another error, of the same type. Here is the full code of the initialize ide function:

Code: Select all

void kide(unsigned int BAR0, unsigned int BAR1, unsigned int BAR2, unsigned int BAR3, unsigned int BAR4)
{
   int i, j, k, m, count = 0;
   
   ide_write(0, ATA_REG_CONTROL, 0x04);
   ide_write(1, ATA_REG_CONTROL, 0x04);
   u8int status = ide_read(i, ATA_REG_STATUS);
   if(!(status & ATA_SR_BSY))
	kputs("Done\n");

   channels[ATA_PRIMARY  ].base  = (BAR0 &= 0xFFFFFFFC) + 0x1F0 * (!BAR0);
   channels[ATA_PRIMARY  ].ctrl  = (BAR1 &= 0xFFFFFFFC) + 0x3F4 * (!BAR1);
   channels[ATA_SECONDARY].base  = (BAR2 &= 0xFFFFFFFC) + 0x170 * (!BAR2);
   channels[ATA_SECONDARY].ctrl  = (BAR3 &= 0xFFFFFFFC) + 0x374 * (!BAR3);
   channels[ATA_PRIMARY  ].bmide = (BAR4 &= 0xFFFFFFFC) + 0; // Bus Master IDE
   channels[ATA_SECONDARY].bmide = (BAR4 &= 0xFFFFFFFC) + 8; // Bus Master IDE

   ide_write(ATA_PRIMARY  , ATA_REG_CONTROL, 2);
   ide_write(ATA_SECONDARY, ATA_REG_CONTROL, 2);

   for (i = 0; i < 2; i++)
      for (j = 0; j < 2; j++) {
 
         unsigned char err = 0, type = IDE_ATA, status;
         ide_devices[count].Reserved = 0; 
 
         ide_write(i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4)); 
         for(m = 0; m < 4; m++)
           ide_read(i, ATA_REG_ALTSTATUS); 
            
         ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
         for(m = 0; m < 4; m++)
             ide_read(i, ATA_REG_ALTSTATUS); 
          
	 status = ide_read(i, ATA_REG_STATUS);
         if (status == 0) continue; 
    
         while(1) {
            status = ide_read(i, ATA_REG_STATUS);  if(!(status & ATA_SR_BSY)){
            if ((status & ATA_SR_ERR)) {err = 1; break;}
            if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break; }
         }
         kputs("Not stuck in infinite loop.\n");
         
         if (err != 0) {
            unsigned char cl = ide_read(i, ATA_REG_LBA1);
            unsigned char ch = ide_read(i, ATA_REG_LBA2);
 
            if (cl == 0x14 && ch ==0xEB)
               type = IDE_PATAPI;
            else if (cl == 0x69 && ch == 0x96)
               type = IDE_SATAPI;
	    else if (cl == 0 && ch == 0)
               type = IDE_PATA;
	    else if (cl==0x3C && ch==0xC3)
               type = IDE_SATA;

            else {
               kputs("Unknown type device found at channel "); // Unknown Type (may not be a device).
   	       kputd(i);
               kputs(", drive ");
               kputd(j);
               kputs(".\n");
	       continue;
	    }

            ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
            for(m = 0; m < 4; m++)
                ide_read(i, ATA_REG_ALTSTATUS); // Reading the Alternate Status port wastes 100ns; loop four times.

         }
 
         ide_read_buffer(i, ATA_REG_DATA, (unsigned int *)ide_buf, 128);
 
         ide_devices[count].Reserved     = 1;
         ide_devices[count].Type         = type;
         ide_devices[count].Channel      = i;
         ide_devices[count].Drive        = j;
         ide_devices[count].Signature    = ((unsigned int )(ide_buf + ATA_IDENT_DEVICETYPE));
         ide_devices[count].Capabilities = ((unsigned int )(ide_buf + ATA_IDENT_CAPABILITIES));
         ide_devices[count].CommandSets  = ((unsigned int )(ide_buf + ATA_IDENT_COMMANDSETS));
 
         if (ide_devices[count].CommandSets & (1 << 26))
            ide_devices[count].Size   = ((unsigned int )(ide_buf + ATA_IDENT_MAX_LBA_EXT));
         else
            ide_devices[count].Size   = ((unsigned int )(ide_buf + ATA_IDENT_MAX_LBA));
 
         for(k = 0; k < 40; k += 2) {
            ide_devices[count].Model[k] = ide_buf[ATA_IDENT_MODEL + k + 1];
            ide_devices[count].Model[k + 1] = ide_buf[ATA_IDENT_MODEL + k];}
         ide_devices[count].Model[40] = 0; // Terminate String.
 
         count++;
   }
 
    for (i = 0; i < 4; i++)
       if (ide_devices[i].Reserved == 1) {
           kputs("Found ");
           kputs((const char *[]){"ATA", "PATAPI", "SATAPI", "PATA", "SATA"}[ide_devices[i].Type]);         /* Type */
           
           kputs("Drive ");
           kputd(ide_devices[i].Size / 1024 / 1024 / 2);               /* Size */
           kputs("GB - ");   
           kputs(ide_devices[i].Model);
           kputs("\n");
    } 
}
Now, it prints the "Done", but doesnt print the "Not Stuck In Infinite Message". This means it get stucks in an infinite message, as I found after debugging.
http://shikhin.in/

Current status: Gandr.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Software Reset on all ATA Drives present on the Bus.

Post by gerryg400 »

What is the value of status that is returned by ide_read() ? You should be easily able to fix this. Print out the value, figure out which bits are set and which are clear. Work out which test should be break;ing. Fix the bug.

You need to spend more than a few minutes on it.
If a trainstation is where trains stop, what is a workstation ?
Laksen
Member
Member
Posts: 140
Joined: Fri Nov 09, 2007 3:30 am
Location: Aalborg, Denmark

Re: Software Reset on all ATA Drives present on the Bus.

Post by Laksen »

If you look at the software reset protocol you need to deassert the bit again after 2 ms or something. And after that you can first check the BSY bit after a delay again
http://j-software.dk | JPasKernel - My Object Pascal kernel
shikhin
Member
Member
Posts: 274
Joined: Sat Oct 09, 2010 3:35 am
Libera.chat IRC: shikhin
Contact:

Re: Software Reset on all ATA Drives present on the Bus.

Post by shikhin »

Laksen wrote:If you look at the software reset protocol you need to deassert the bit again after 2 ms or something. And after that you can first check the BSY bit after a delay again
You mean, I need to clear the Software Reset bit after 2ms or something? Or set it again?
http://shikhin.in/

Current status: Gandr.
shikhin
Member
Member
Posts: 274
Joined: Sat Oct 09, 2010 3:35 am
Libera.chat IRC: shikhin
Contact:

Re: Software Reset on all ATA Drives present on the Bus.

Post by shikhin »

Well, I considered my code, read loads of things on the reset standard, and here is my revised code:

Code: Select all

void kide(unsigned int BAR0, unsigned int BAR1, unsigned int BAR2, unsigned int BAR3, unsigned int BAR4)
{
   int i, j, k, m, count = 0;
   
   channels[ATA_PRIMARY  ].base  = (BAR0 &= 0xFFFFFFFC) + 0x1F0 * (!BAR0);
   channels[ATA_PRIMARY  ].ctrl  = (BAR1 &= 0xFFFFFFFC) + 0x3F4 * (!BAR1);
   channels[ATA_SECONDARY].base  = (BAR2 &= 0xFFFFFFFC) + 0x170 * (!BAR2);
   channels[ATA_SECONDARY].ctrl  = (BAR3 &= 0xFFFFFFFC) + 0x374 * (!BAR3);
   channels[ATA_PRIMARY  ].bmide = (BAR4 &= 0xFFFFFFFC) + 0; // Bus Master IDE
   channels[ATA_SECONDARY].bmide = (BAR4 &= 0xFFFFFFFC) + 8; // Bus Master IDE
   
   ide_write(ATA_PRIMARY  , ATA_REG_CONTROL, 0x04);
   ide_write(ATA_SECONDARY, ATA_REG_CONTROL, 0x04);
   kwait(4);

   ide_write(ATA_PRIMARY  , ATA_REG_CONTROL, 0x00);
   ide_write(ATA_SECONDARY, ATA_REG_CONTROL, 0x00);
   kwait(4);    
     
   u8int status = ide_read(ATA_PRIMARY, ATA_REG_STATUS);
   if(!(status & ATA_SR_BSY))
	kputs("Done 1\t");
   kputh(status);
   kputs("\n");

   u8int status2 = ide_read(ATA_SECONDARY, ATA_REG_STATUS);
   if(!(status2 & ATA_SR_BSY))
	kputs("\nDone 2\t");
   kputh(status2);
   kputs("\n");

   for (i = 0; i < 2; i++)
      for (j = 0; j < 2; j++) {
 
         unsigned char err = 0, type = IDE_ATA, status;
         ide_devices[count].Reserved = 0; 
 
         ide_write(i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4)); 
         for(m = 0; m < 4; m++)
           ide_read(i, ATA_REG_ALTSTATUS); 
            
         ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
         for(m = 0; m < 4; m++)
             ide_read(i, ATA_REG_ALTSTATUS); 
          
	 status = ide_read(i, ATA_REG_STATUS);
         if (status == 0) continue; 
         kputh(status);
         while(1) {
            status = ide_read(i, ATA_REG_STATUS);  if(!(status & ATA_SR_BSY)){
            if ((status & ATA_SR_ERR)) {err = 1; break;}
            if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break; }
         }
         kputs("Not stuck in infinite loop.\n");
         
         if (err != 0) {
            unsigned char cl = ide_read(i, ATA_REG_LBA1);
            unsigned char ch = ide_read(i, ATA_REG_LBA2);
  
            if (cl == 0x14 && ch ==0xEB)
               type = IDE_PATAPI;
            else if (cl == 0x69 && ch == 0x96)
               type = IDE_SATAPI;
	    else if (cl == 0 && ch == 0)
               type = IDE_PATA;
	    else if (cl==0x3C && ch==0xC3)
               type = IDE_SATA;

            else {
               kputs("Unknown type device found at channel "); // Unknown Type (may not be a device).
   	       kputd(i);
               kputs(", drive ");
               kputd(j);
               kputs(".\n");
	       continue;
	    }

            ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
            for(m = 0; m < 4; m++)
                ide_read(i, ATA_REG_ALTSTATUS); // Reading the Alternate Status port wastes 100ns; loop four times.

         }
 
         ide_read_buffer(i, ATA_REG_DATA, (unsigned int *)ide_buf, 128);
 
         ide_devices[count].Reserved     = 1;
         ide_devices[count].Type         = type;
         ide_devices[count].Channel      = i;
         ide_devices[count].Drive        = j;
         ide_devices[count].Signature    = ((unsigned int )(ide_buf + ATA_IDENT_DEVICETYPE));
         ide_devices[count].Capabilities = ((unsigned int )(ide_buf + ATA_IDENT_CAPABILITIES));
         ide_devices[count].CommandSets  = ((unsigned int )(ide_buf + ATA_IDENT_COMMANDSETS));
 
         if (ide_devices[count].CommandSets & (1 << 26))
            ide_devices[count].Size   = ((unsigned int )(ide_buf + ATA_IDENT_MAX_LBA_EXT));
         else
            ide_devices[count].Size   = ((unsigned int )(ide_buf + ATA_IDENT_MAX_LBA));
 
         for(k = 0; k < 40; k += 2) {
            ide_devices[count].Model[k] = ide_buf[ATA_IDENT_MODEL + k + 1];
            ide_devices[count].Model[k + 1] = ide_buf[ATA_IDENT_MODEL + k];}
         ide_devices[count].Model[40] = 0; // Terminate String.
 
         count++;
   }
 
    for (i = 0; i < 4; i++)
       if (ide_devices[i].Reserved == 1) {
           kputs("Found ");
           kputs((const char *[]){"ATA", "PATAPI", "SATAPI", "PATA", "SATA"}[ide_devices[i].Type]);         /* Type */
           
           kputs("Drive ");
           kputd(ide_devices[i].Size / 1024 / 1024 / 2);               /* Size */
           kputs("GB - ");   
           kputs(ide_devices[i].Model);
           kputs("\n");
    } 
}
Well, the result produced was the following:
FF
FF
FF
With no dones written, and also the system got stuck into a infinite loop. I dont understand why after the software reset, all the bits on the status Byte are set? I dont understand this, and is this normal?
http://shikhin.in/

Current status: Gandr.
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

Re: Software Reset on all ATA Drives present on the Bus.

Post by bewing »

No, it is not normal. 0xff is generally an illegal result for reading any IO port, and is definitely an illegal value for the ATA status port. Generally if you get an FF it means you are reading the wrong port.
shikhin
Member
Member
Posts: 274
Joined: Sat Oct 09, 2010 3:35 am
Libera.chat IRC: shikhin
Contact:

Re: Software Reset on all ATA Drives present on the Bus.

Post by shikhin »

bewing wrote:No, it is not normal. 0xff is generally an illegal result for reading any IO port, and is definitely an illegal value for the ATA status port. Generally if you get an FF it means you are reading the wrong port.
Sir, I found out the ports, I was writing to, cross checked them, and AFAIK they are all correct. I dont know where the problem lies sir. here are my read and write function:

Code: Select all

unsigned char ide_read(unsigned char channel, unsigned char reg)
{
    u8int result;
    if (reg > 0x07 && reg < 0x0C)
       ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
    if (reg < 0x08)
       result = inb(channels[channel].base + reg - 0x00);
    else if (reg < 0x0C)
       result = inb(channels[channel].base  + reg - 0x06);
    else if (reg < 0x0E)
       result = inb(channels[channel].ctrl  + reg - 0x0A);
    else if (reg < 0x16)
       result = inb(channels[channel].bmide + reg - 0x0E);
    if (reg > 0x07 && reg < 0x0C)
       ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
    return result;
}

void ide_write(unsigned char channel, unsigned char reg, unsigned char data)
{
    if (reg > 0x07 && reg < 0x0C)
       ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
    if (reg < 0x08)
       outb(data, channels[channel].base  + reg - 0x00);
    else if (reg < 0x0C)
       outb(data, channels[channel].base  + reg - 0x06);
    else if (reg < 0x0E)
       outb(data, channels[channel].ctrl  + reg - 0x0A);
    else if (reg < 0x16)
       outb(data, channels[channel].bmide + reg - 0x0E);
    if (reg > 0x07 && reg < 0x0C)
       ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
}

void ide_read_buffer(unsigned char channel, unsigned char reg, unsigned int *buffer, unsigned int quads)
{
    if (reg > 0x07 && reg < 0x0C)
        ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
    asm("pushw %es; movw %ds, %ax; movw %ax, %es");
    if (reg < 0x08)
        insl(channels[channel].base  + reg - 0x00, (void*)buffer, quads);
    else if (reg < 0x0C)
        insl(channels[channel].base  + reg - 0x06, (void*)buffer, quads);
    else if (reg < 0x0E)
        insl(channels[channel].ctrl  + reg - 0x0A, (void*)buffer, quads);
    else if (reg < 0x16)
        insl(channels[channel].bmide + reg - 0x0E, (void*)buffer, quads);
    asm("popw %es;");
    if (reg > 0x07 && reg < 0x0C)
        ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
}
Here are the macros used:

Code: Select all

#define ATA_SR_BSY     0x80
#define ATA_SR_DRDY    0x40
#define ATA_SR_DF      0x20
#define ATA_SR_DSC     0x10
#define ATA_SR_DRQ     0x08
#define ATA_SR_CORR    0x04
#define ATA_SR_IDX     0x02
#define ATA_SR_ERR     0x01

#define ATA_ER_BBK      0x80
#define ATA_ER_UNC      0x40
#define ATA_ER_MC       0x20
#define ATA_ER_IDNF     0x10
#define ATA_ER_MCR      0x08
#define ATA_ER_ABRT     0x04
#define ATA_ER_TK0NF    0x02
#define ATA_ER_AMNF     0x01

#define ATA_CMD_READ_PIO          0x20
#define ATA_CMD_READ_PIO_EXT      0x24
#define ATA_CMD_READ_DMA          0xC8
#define ATA_CMD_READ_DMA_EXT      0x25
#define ATA_CMD_WRITE_PIO         0x30
#define ATA_CMD_WRITE_PIO_EXT     0x34
#define ATA_CMD_WRITE_DMA         0xCA
#define ATA_CMD_WRITE_DMA_EXT     0x35
#define ATA_CMD_CACHE_FLUSH       0xE7
#define ATA_CMD_CACHE_FLUSH_EXT   0xEA
#define ATA_CMD_PACKET            0xA0
#define ATA_CMD_IDENTIFY_PACKET   0xA1
#define ATA_CMD_IDENTIFY          0xEC

#define ATAPI_CMD_READ       0xA8
#define ATAPI_CMD_EJECT      0x1B

#define ATA_IDENT_DEVICETYPE   0
#define ATA_IDENT_CYLINDERS    2
#define ATA_IDENT_HEADS        6
#define ATA_IDENT_SECTORS      12
#define ATA_IDENT_SERIAL       20
#define ATA_IDENT_MODEL        54
#define ATA_IDENT_CAPABILITIES 98
#define ATA_IDENT_FIELDVALID   106
#define ATA_IDENT_MAX_LBA      120
#define ATA_IDENT_COMMANDSETS  164
#define ATA_IDENT_MAX_LBA_EXT  200

#define IDE_ATA        0x00
#define IDE_PATAPI     0x01
#define IDE_SATAPI     0x02
#define IDE_PATA       0x03
#define IDE_SATA       0x04
  
#define ATA_MASTER     0x00
#define ATA_SLAVE      0x01

#define ATA_REG_DATA       0x00
#define ATA_REG_ERROR      0x01
#define ATA_REG_FEATURES   0x01
#define ATA_REG_SECCOUNT0  0x02
#define ATA_REG_LBA0       0x03
#define ATA_REG_LBA1       0x04
#define ATA_REG_LBA2       0x05
#define ATA_REG_HDDEVSEL   0x06
#define ATA_REG_COMMAND    0x07
#define ATA_REG_STATUS     0x07
#define ATA_REG_SECCOUNT1  0x08
#define ATA_REG_LBA3       0x09
#define ATA_REG_LBA4       0x0A
#define ATA_REG_LBA5       0x0B
#define ATA_REG_CONTROL    0x0C
#define ATA_REG_ALTSTATUS  0x0C
#define ATA_REG_DEVADDRESS 0x0D

#define ATA_PRIMARY        0x00
#define ATA_SECONDARY      0x01
 
#define ATA_READ           0x00
#define ATA_WRITE          0x01
Can anybody help me, by telling me, what is wrong with the code. Am I reading a invalid port? AFAIK, its all right. #-o
http://shikhin.in/

Current status: Gandr.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: Software Reset on all ATA Drives present on the Bus.

Post by gerryg400 »

Not sure what's wrong with your code, but may I say that I think it's too complex? ide_read calls ide_write, ide_write calls itself recursively. All those hardcoded tests for the value of reg. Simple code is far easier to debug. I would never be confident in code structured like this.

Restructure your code in a more regular layered fashion. Since you are dealing directly with hardware, build up your primitives from the bottom up. Document each primitive and test each one as you go. You'll have a much better chance of success.

Begin by writing directly to the port using outb and see what happens.
If a trainstation is where trains stop, what is a workstation ?
User avatar
Owen
Member
Member
Posts: 1700
Joined: Fri Jun 13, 2008 3:21 pm
Location: Cambridge, United Kingdom
Contact:

Re: Software Reset on all ATA Drives present on the Bus.

Post by Owen »

Code: Select all

    asm("pushw %es; movw %ds, %ax; movw %ax, %es");
    asm("popw %es;");
GCC will crap itself over that. You cannot touch the stack from inline asm.

Also, why might %es != %ds? GCC will also crap itself over that.

Code: Select all

    if (reg < 0x08)
        insl(channels[channel].base  + reg - 0x00, (void*)buffer, quads);
    else if (reg < 0x0C)
        insl(channels[channel].base  + reg - 0x06, (void*)buffer, quads);
    else if (reg < 0x0E)
        insl(channels[channel].ctrl  + reg - 0x0A, (void*)buffer, quads);
    else if (reg < 0x16)
        insl(channels[channel].bmide + reg - 0x0E, (void*)buffer, quads);
insl? ATA doesn't like dword reads.
Post Reply