Page 1 of 1
Software Reset on all ATA Drives present on the Bus.
Posted: Fri Nov 19, 2010 7:49 am
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?
Re: Software Reset on all ATA Drives present on the Bus.
Posted: Fri Nov 19, 2010 8:14 am
by gerryg400
Reset is bit 2, but bit 2 is the third bit. You should write 0x4.
Re: Software Reset on all ATA Drives present on the Bus.
Posted: Fri Nov 19, 2010 8:35 am
by shikhin
Hi,
Oh. I thought that the second bit, was bit 2.

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.
Re: Software Reset on all ATA Drives present on the Bus.
Posted: Fri Nov 19, 2010 8:49 am
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.
Re: Software Reset on all ATA Drives present on the Bus.
Posted: Fri Nov 19, 2010 9:32 am
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
Re: Software Reset on all ATA Drives present on the Bus.
Posted: Fri Nov 19, 2010 11:46 pm
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?
Re: Software Reset on all ATA Drives present on the Bus.
Posted: Sat Nov 20, 2010 7:05 am
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?
Re: Software Reset on all ATA Drives present on the Bus.
Posted: Sat Nov 20, 2010 1:07 pm
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.
Re: Software Reset on all ATA Drives present on the Bus.
Posted: Sat Nov 20, 2010 11:51 pm
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.

Re: Software Reset on all ATA Drives present on the Bus.
Posted: Sun Nov 21, 2010 12:12 am
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.
Re: Software Reset on all ATA Drives present on the Bus.
Posted: Sun Nov 21, 2010 5:18 am
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.