Ports for SATA programming

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
sebihepp
Member
Member
Posts: 194
Joined: Tue Aug 26, 2008 11:24 am
GitHub: https://github.com/sebihepp

Ports for SATA programming

Post by sebihepp »

Hello,

I have read the article about PATA/SATA and PATAPI/SATAPI and successfully implemented a routine
that opens the tray of a cdrom drive, after detecting all drives. It works on Bochs and on an machine
with an old IDE-Port.

Now I want to access SATA/SATAPI drives, because all my other PCs have only SATA-Ports. Even the
one with one IDE-Port has SATA-HDs and uses the IDE for a cdrom drive.

I read the source of ATADRVR from http://www.ata-atapi.com and from the wiki. But I don't get it to
work. First I check PCI, but only on one computer I find an ATA-Controller. And there I find a SATAPI
Device, but everytime I send IDENTIFY PACKET DEVICE my system waits for BSY to Clear and loops
infinitly. And If I press Open on the drive, nothing happens. It seems that the drive never finishes that
Command.
Next I try to access standard IDE ports and then the alternate IDE ports. On all PCs with only SATA I
find nothing. Only the PC with the IDE port shows his PATAPI device correctly. So neither on PCI nor
on ports I find devices.

The BIOS on all computers is set to configure all PCI devices during boot.

I will post code later, if necessary. I don't think it helps if I post just a bunch of uncommented code.

best regards
sebihepp
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Re: Ports for SATA programming

Post by pcmattman »

You usually communicate with SATA drives through AHCI if the system isn't in IDE compatibility mode (a BIOS setting, usually). The AHCI specification is free from Intel, and you can purchase the SATA specification.

Failing that there is a SATA.HTM file in ATADRVR.
sebihepp
Member
Member
Posts: 194
Joined: Tue Aug 26, 2008 11:24 am
GitHub: https://github.com/sebihepp

Re: Ports for SATA programming

Post by sebihepp »

Thanks for the idea with AHCI. I checked it, but every pc was set to IDE mode.
That is my code for detecting an ATAPI drive and then open the tray.
Before calling this routine, I search through PCI and set IDE0 to IDE3 to the corresponding ports.
If I found no PCI device I set them to 0x1F0, 0x3F4, ...
inb and outb have a delay integrated (read 4 times from port 0xED).
Interrupts are working and IRQ0E is a routine, wich increments IDECounter by one.
I think I said everything, but just ask if I forgot to mention something.

On my main computer I get following output:

Code: Select all

IDE: Channel 0x0000EC00, Master, 0x0000E880
IDE: No Device found!
IDE: Channel 0x0000EC00, Slave, 0x0000E880
IDE: No Device found!
IDE: Channel 0x0000E800, Master, 0x0000E480
Then the entire machine hangs and the cdrom drive rotates but doesn't open the tray even if I push
the button on the drive. I think BSY never get cleared and the drive hang.

Code: Select all

void CKernel::AtapiTest(void)
{
	unsigned int Start = 0;
	unsigned char Buffer[2048];
	unsigned char Slave = 0;
	unsigned short Port = 0x1F0;
	unsigned short Port2 = 0x3F4;
	bool ATAPIFound = false;

	if (IDEIRQ == 14)
	{
		IRH.RegisterInterruptHandler(0x2E, (void*)IRQ0E);
		IRH.RegisterInterruptHandler(0x2F, (void*)IRQ0E);
		IRH.EnableIRQ(0xE);
		IRH.EnableIRQ(0xF);
	} else {
		IRH.RegisterInterruptHandler(0x20 + IDEIRQ, (void*)IRQ0E);
		IRH.EnableIRQ(IDEIRQ);
	}

	//Deleting HOB-Bit and Disable Interrupts
	outb(IDE1+2, 0x02);
	outb(IDE3+2, 0x02);

	//Searching for ATAPI Device
	for (int i = 0; i < 5; ++i)
	{
		switch (i)
		{

			//On my PC these are the SATA Ports
			case 0:
				Port = IDE0;
				Port2 = IDE1;
				Slave = 0;
				break;
			case 1:
				Port = IDE0;
				Port2 = IDE1;
				Slave = 1;
				break;
			case 2:
				Port = IDE2;
				Port2 = IDE3;
				Slave = 0;
				break;
			case 3:
				Port = IDE2;
				Port2 = IDE3;
				Slave = 1;
				break;

			default:
				Video.Print((char*)"No ATAPI Device found!\n");
				if (IDE0 == 0x1E8)
				{
					return;
				}
				if (IDE0 != 0x1F0)
				{
					Video.Print((char*)"\nTesting again with standard ports!\n");
					IDE0 = 0x1F0;
					IDE1 = 0x3F4;
					IDE2 = 0x170;
					IDE3 = 0x374;

					Keyboard.Clear();
					while (!Keyboard.IsKeyAvailable());
					Keyboard.Clear();

					AtapiTest();
				} else {
					Video.Print((char*)"\nTesting again with 2nd standard ports!\n");
					IDE0 = 0x1E8;
					IDE1 = 0x3EC;
					IDE2 = 0x168;
					IDE3 = 0x36C;

					Keyboard.Clear();
					while (!Keyboard.IsKeyAvailable());
					Keyboard.Clear();

					AtapiTest();
				}
				return;
		}

		Video.Print((char*)"IDE: Channel ");
		Video.PrintIntAsHex(Port);
		if (Slave)
		{
			Video.Print((char*)", Slave, ");
		} else {
			Video.Print((char*)", Master, ");
		}
		Video.PrintIntAsHex(Port2);
		Video.PrintCh('\n');

		//Select Device and test for floating bus
		outb(Port+6, 0xA0 | (Slave << 4));
		Start = TimerCounter;
		while (TimerCounter < (Start+2));
		//Test floating bus
		if ((inb(Port+7) == 0xFF) || (inb(Port+7) == 0x7F) ||
			(inb(Port+7) == 0x00))
		{
			Video.Print((char*)"IDE: No Device found!\n");
			continue;
		}

		//Test if there is any device
		outb(Port+2, 0x55);
		outb(Port+3, 0xAA);IDE: Channel 0x0000EC00, Master, 0x0000E880
		outb(Port+2, 0xAA);
		outb(Port+3, 0x55);
		outb(Port+2, 0x55);
		outb(Port+3, 0xAA);
		if ((inb(Port+2) != 0x55) || 
			(inb(Port+3) != 0xAA))
		{
			Video.Print((char*)"IDE: No Device found!\n");
			continue;
		}

		//Command: IDENTIFY PACKET DEVICE
		outb(Port+7, 0xA1);
		//Wait for BSY == 0
		while (inb(Port+7) & 0x80);
		//Check for Errors
		if ((inb(Port+7) & 0x61) == 0x40)
		{
			Video.Print((char*)"IDE: ATAPI Device found!\n");
			break;
		}
		Video.Print((char*)"IDE: Error!\n");
	}

	//Get Info
	for (unsigned int i = 0; i < 256; ++i)
	{
		((unsigned short*)Buffer)[i] = inw(Port);
	}

	//Print Model
	Video.Print((char*)"IDE: Model=");
	for (int i = 54; i < 54+40; i+=2)
	{
		Video.PrintCh(Buffer[i+1]);
		Video.PrintCh(Buffer[i]);
	}
	Video.Print((char*)"\n");

	//Enable Interrupts
	outb(Port2+2, 0x00);
	//Eject Media
	IDECounter = 0;
	outb(Port+7, 0xA0);
	//Wait for BSY == 0
	while (inb(Port+7) & 0x80);
	//Check for Errors
	if ((inb(Port+7) & 0x01) || (inb(Port+7) & 0x20) ||
		!(inb(Port+7) & 0x40))
	{
		Video.Print((char*)"IDE: PACKET Error\n");
		return;
	}
	//Send Command Words
	outw(Port, 0x001B);
	outw(Port, 0x0000);
	outw(Port, 0x0002);
	outw(Port, 0x0000);
	outw(Port, 0x0000);
	outw(Port, 0x0000);

	//Wait for Interrupt
	while (!IDECounter);
	IDECounter = 0;

	//Check for Errors
	if ((inb(Port+7) & 0x01) || (inb(Port+7) & 0x20) ||
		!(inb(Port+7) & 0x40))
	{
		Video.Print((char*)"IDE: Eject Media Error\n");
		return;
	}

	//Done
	Video.Print((char*)"IDE: Eject Media Done\n");
}
I think I buy a PCI card with 2 UDMA Controllers and Ports and a cheap 80GByte IDE harddisk
for my testing pc. Only need the money. =)

Best regards
sebihepp
sebihepp
Member
Member
Posts: 194
Joined: Tue Aug 26, 2008 11:24 am
GitHub: https://github.com/sebihepp

Re: Ports for SATA programming

Post by sebihepp »

What a pity, the pc where it works is destroyed. =(
But I found out that I have to set SATA-Mode to "Compability" instead of "Enhanced".

On the main pc with cd-drive on IDE port and harddisks on SATA ports I got a step forward.
So, here are the specs:
Main pc - cd-drive on IDE port, harddisks on SATA port 1 and 4
Second pc - cd-drive on SATA port 6, harddisk on SATA port 1
That is my routine printing:
Main pc - IDE Controller found on PCI, IDE Controller(PCI) device 1,2 and 4 = No Device device 3 = Not Ready
----Primary IDE(Ports 0x170,...) device 1 = Error, device 2, 3 and 4 = No Device
----Secondary IDE device 1, 2, 3 and 4 = No Device
Second pc - Nothing found on PCI,
----Primary IDE(Ports 0x170,...) device 1 = Error, device 2, 3 and 4 = No Device
----Secondary IDE device 1, 2, 3 and 4 = No Device

Why is the cd-drive on main pc(PATAPI) not ready? Here is my current piece of code:

Code: Select all

void CKernel::AtapiTest(void)
{
	unsigned int Start = 0;
	unsigned char Buffer[2048];
	unsigned char Slave = 0;
	unsigned short Port = 0x1F0;
	unsigned short Port2 = 0x3F4;
	bool ATAPIFound = false;
	bool Timeout = false;

	if (IDEIRQ == 14)
	{
		IRH.RegisterInterruptHandler(0x2E, (void*)IRQ0E);
		IRH.RegisterInterruptHandler(0x2F, (void*)IRQ0E);
		IRH.EnableIRQ(0xE);
		IRH.EnableIRQ(0xF);
	} else {
		IRH.RegisterInterruptHandler(0x20 + IDEIRQ, (void*)IRQ0E);
		IRH.EnableIRQ(IDEIRQ);
	}

	//Deleting HOB-Bit and Disable Interrupts
	outb(IDE1+2, 0x02);
	outb(IDE3+2, 0x02);

	//Searching for ATAPI Device
	for (int i = 0; i < 5; ++i)
	{
		switch (i)
		{

			//On my PC these are the SATA Ports
			case 0:
				Port = IDE0;
				Port2 = IDE1;
				Slave = 0;
				break;
			case 1:
				Port = IDE0;
				Port2 = IDE1;
				Slave = 1;
				break;
			case 2:
				Port = IDE2;
				Port2 = IDE3;
				Slave = 0;
				break;
			case 3:
				Port = IDE2;
				Port2 = IDE3;
				Slave = 1;
				break;

			default:
				Video.Print((char*)"No ATAPI Device found!\n");
				if (IDE0 == 0x1E8)
				{
					return;
				}
				if (IDE0 != 0x1F0)
				{
					Video.Print((char*)"\nTesting again with standard ports!\n");
					IDE0 = 0x1F0;
					IDE1 = 0x3F4;
					IDE2 = 0x170;
					IDE3 = 0x374;
					IDEIRQ = 14;

					Keyboard.Clear();
					while (!Keyboard.IsKeyAvailable());
					Keyboard.Clear();

					AtapiTest();
				} else {
					Video.Print((char*)"\nTesting again with 2nd standard ports!\n");
					IDE0 = 0x1E8;
					IDE1 = 0x3EC;
					IDE2 = 0x168;
					IDE3 = 0x36C;
					IDEIRQ = 14;

					Keyboard.Clear();
					while (!Keyboard.IsKeyAvailable());
					Keyboard.Clear();

					AtapiTest();
				}
				return;
		}

		Video.Print((char*)"IDE: Channel ");
		Video.PrintIntAsHex(Port);
		if (Slave)
		{
			Video.Print((char*)", Slave, ");
		} else {
			Video.Print((char*)", Master, ");
		}
		Video.PrintIntAsHex(Port2);
		Video.PrintCh('\n');

		//Select Device and test for floating bus
		outb(Port+6, 0xA0 | (Slave << 4));
		Start = TimerCounter;
		while (TimerCounter < (Start+2));
		//Test floating bus
		if ((inb(Port+7) == 0xFF) || (inb(Port+7) == 0x7F) ||
			(inb(Port+7) == 0x00))
		{
			Video.Print((char*)"IDE: No Device found!\n");
			continue;
		}

		//Test if there is any device
		outb(Port+2, 0x55);
		outb(Port+3, 0xAA);
		outb(Port+2, 0xAA);
		outb(Port+3, 0x55);
		outb(Port+2, 0x55);
		outb(Port+3, 0xAA);
		if ((inb(Port+2) != 0x55) || 
			(inb(Port+3) != 0xAA))
		{
			Video.Print((char*)"IDE: No Device found!\n");
			continue;
		}

		//Make a Software Reset
		outb(Port2+2, 0x06);
		Start = TimerCounter;
		while (TimerCounter < (Start+2));
		outb(Port2+2, 0x02);
		Start = TimerCounter;
		while (TimerCounter < (Start+2));

		//Wait for Device Ready
		Timeout = false;
		Start = TimerCounter;
		while ((inb(Port+7) & 0x80))
		{
			if (TimerCounter > Start+100)
			{
				Timeout = true;
				break;
			}
		}
		if (Timeout)
		{
			Video.Print((char*)"IDE: Device not Responding!\n");
			continue;
		}
		if (!(inb(Port+7) & 0x40))
		{
			Video.Print((char*)"IDE: Device not Ready!\n");
			continue;
		}

		//Command: IDENTIFY PACKET DEVICE
		outb(Port+7, 0xA1);
		//Wait for BSY == 0
		Timeout = false;
		Start = TimerCounter;
		while (inb(Port+7) & 0x80)
		{
			if (TimerCounter > Start+20)
			{
				Timeout = true;
				break;
			}
		}

		//Check if all okay
		if (!(inb(Port+7) & 0x21) && !(Timeout))
		{
			Video.Print((char*)"IDE: ATAPI Device found!\n");
			break;
		}
		Video.Print((char*)"IDE: Error!\n");
	}

	//Get Info
	for (unsigned int i = 0; i < 256; ++i)
	{
		((unsigned short*)Buffer)[i] = inw(Port);
	}

	//Print Model
	Video.Print((char*)"IDE: Model=");
	for (int i = 54; i < 54+40; i+=2)
	{
		Video.PrintCh(Buffer[i+1]);
		Video.PrintCh(Buffer[i]);
	}
	Video.Print((char*)"\n");

	//Enable Interrupts
	outb(Port2+2, 0x00);
	//Eject Media
	IDECounter = 0;
	outb(Port+7, 0xA0);
	//Wait for BSY == 0
	Timeout = false;
	Start = TimerCounter;
	while (inb(Port+7) & 0x80)
	{
		if (TimerCounter > Start+20)
		{
			Timeout = true;
			break;
		}
	}
	//Check for Errors
	if ((inb(Port+7) & 0x01) || (inb(Port+7) & 0x20) ||
		(Timeout))
	{
		Video.Print((char*)"IDE: PACKET Error\n");
		return;
	}
	//Send Command Words
	outw(Port, 0x001B);
	outw(Port, 0x0000);
	outw(Port, 0x0002);
	outw(Port, 0x0000);
	outw(Port, 0x0000);
	outw(Port, 0x0000);

	//Wait for Interrupt
	while (!IDECounter);
	IDECounter = 0;

	//Check for Errors
	if ((inb(Port+7) & 0x01) || (inb(Port+7) & 0x20))
	{
		Video.Print((char*)"IDE: Eject Media Error\n");
		return;
	}

	//Done
	Video.Print((char*)"IDE: Eject Media Done\n");
}
best regards
sebihepp
sebihepp
Member
Member
Posts: 194
Joined: Tue Aug 26, 2008 11:24 am
GitHub: https://github.com/sebihepp

Re: Ports for SATA programming

Post by sebihepp »

Oh, and I upload an iso-file.
if you burn this on cd you can boot my kernel from there. Would be nice if some of you could
test too and let me know what results you are having. Also on the cd there is the complete source.

http://www.gatewayheaven.com/~sebihepp/heidelberg.iso
sebihepp
Member
Member
Posts: 194
Joined: Tue Aug 26, 2008 11:24 am
GitHub: https://github.com/sebihepp

Re: Ports for SATA programming

Post by sebihepp »

Okay, I debugged many times the last days and I think I found the problem.
First of all, I have to ignore Device Ready (DRDY) by ATAPI devices, because they doesn't
inplement this bit correctly, to avoid the BIOS detecting them as ATA device.

After sending the Identify packed device command, I wait for about 0.1 sec.

Code: Select all

  //Sending Identify Packet Device command
  outb(Port+7, 0xA1);
  Start = TimerCounter;
// On the line below it loops forever
  while (TimerCounter < (Start+2));
TimerCounter is increased by ISR 0. Therefore I think after sending the command the device sends
me an interrupt wich I don't handle correctly. I don't know what to do, because I think everything I do
is right.

ISR 2 does only an iret.
ISR 10, 11, 14 and 15 only increase IDECounter and sends an EOI to the Slave and then to the Master PIC.
Interrupts are enabled and unmasked only the ones I use.
I have checked that the pci-device has assigned 11 as IRQ.
Anyone any idea?

best regards
sebihepp
ru2aqare
Member
Member
Posts: 342
Joined: Fri Jul 11, 2008 5:15 am
Location: Hungary

Re: Ports for SATA programming

Post by ru2aqare »

sebihepp wrote:

Code: Select all

// On the line below it loops forever
  while (TimerCounter < (Start+2));
Is TimerCounter marked as volatile?
sebihepp
Member
Member
Posts: 194
Joined: Tue Aug 26, 2008 11:24 am
GitHub: https://github.com/sebihepp

Re: Ports for SATA programming

Post by sebihepp »

Yes, it is.
And TimerCounter works untill I send the command.

This Problem only occur on my pc, where the IDE-Controller appears as PCI device.
On the other computer (wich is broken now) it works without any problems. And every
other has SATA devices wich my routine doesn't detect.
sebihepp
Member
Member
Posts: 194
Joined: Tue Aug 26, 2008 11:24 am
GitHub: https://github.com/sebihepp

Re: Ports for SATA programming

Post by sebihepp »

Okay, I found my mistake in the ISRs. Now ISR 2 send EOI only to Master PIC and all ISRs > 7 only to Slave PIC.

But now I get the next problem.
In the old sourcecode got no interrupt after sending the packet.
In my new version I get an Exception 06 - undefined opcode at 0x08:0x023???
But this happens only on my main pc. In Bochs everything works fine.

Code: Select all

	IDECounter = 0;
	//Enable Interrupts
	outb(Port2+2, 0x00);
	//Eject Media
	outb(Port+7, 0xA0);
	//Wait for BSY == 0
	Timeout = false;
	Start = TimerCounter;
	while (inb(Port+7) & 0x80)
	{
		if (TimerCounter > Start+20)
		{
			Timeout = true;
			break;
		}
	}
	//Check for Errors
	if ((inb(Port+7) & 0x01) || (inb(Port+7) & 0x20) ||
		(Timeout))
	{
		Video.Print((char*)"IDE: PACKET Error\n");
		return;
	}
	//Send Command Words
	outw(Port, 0x001B);
	outw(Port, 0x0000);
	outw(Port, 0x0002);
	outw(Port, 0x0000);
	outw(Port, 0x0000);
	outw(Port, 0x0000);

	Video.Print((char*)"IDE: PACKET Sent!\n");

	//Wait for BSY == 0
	Timeout = false;
	Start = TimerCounter;
	while (inb(Port+7) & 0x80)
	{
		if (TimerCounter > Start+20)
		{
			Timeout = true;
			break;
		}
	}
	//Check for Errors
	if ((inb(Port+7) & 0x01) || (inb(Port+7) & 0x20) ||
		(Timeout))
	{
		Video.Print((char*)"IDE: Eject Media Error\n");
		return;
	}

	//Done
	Video.Print((char*)"IDE: Eject Media Done\n");
}
I would be very happy if anyone could test my programm and let me know, if it works or which errors
appears. The link is at one post above. And here is an floppy image
http://www.gatewayheaven.com/~sebihepp/ ... floppy.img

sebihepp
sebihepp
Member
Member
Posts: 194
Joined: Tue Aug 26, 2008 11:24 am
GitHub: https://github.com/sebihepp

Re: Ports for SATA programming

Post by sebihepp »

Huh, if I use a cd it works. But if I use a floppy, I got the Exception 6 - undefined opcode???
sebihepp
Member
Member
Posts: 194
Joined: Tue Aug 26, 2008 11:24 am
GitHub: https://github.com/sebihepp

Re: Ports for SATA programming

Post by sebihepp »

That is strange.
First I thought the floppy is broken. I used a new floppy and... it doesn't work. Then I tried the cd again... It doesn't work.

I tried many times, and that is what I found out:
I never recieve an interrupt from IDE. Neither on cd nor on floppy.
On floppy 7 of 10 times I get an Exception 6 - undefined opcode and 3 of 10 times I get an error of the drive
(Error Bit is set or Busy isn't clear after at least 5 sec)
On cd 1 of 3 times it works. another 1 of 3 times I get an error of the device (see above) and the last 1 of 3 times
I get an Exception 6 - undefined opcode

I go to bed soon. I really don't know, what causes that strange behaviour.
Can it be, that it has to do with the missing chars I get in bochs sometimes?
I posted that in an other thread. Sometimes in Bochs it look like either a char is not drawn or
the cursor position (in my video class a integer wich holds the current position) isn't increased.

Bye and good night
sebihepp
Post Reply