⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ata_if.c

📁 Yet Another MP3 Player source code. It can be usefull as reference source for any project.
💻 C
字号:
/*
  Copyright (C) 2001 Jesper Hansen <jesperh@telia.com>.

  Rewritten by:	Nikolai Vorontsov <nickviz@mail.be>

  This file is part of the yampp system.

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either version 2
  of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software Foundation, 
  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include <io.h>
#include <interrupt.h>
#include <progmem.h>

#include "ata_if.h"
#include "delay.h"
#include "mem.h"
#include "uart.h"

// To speed up disk read
#define ASM_OPT


static u08  SetAddress(u08 addr);
static u08  ReadBYTE(u08 addr);
static void WriteBYTE(u08 addr, u08 dat);
static u08  ExecuteCommand(u08 NumSectors, u08 *Buffer);
static void CheckBusy(void);
static void CheckDataReady(void);


//----------------------------------------------------------------------------
// Init hard drive
//----------------------------------------------------------------------------
void ATA_Init(void)
{
  	WriteBYTE(CMD_A + CMD_DEV_HEAD, DH_CHS0);	// Set Drive/Head register, 
											// ie. select drive 0
	CheckBusy();							// Wait for !BUSY
}


//----------------------------------------------------------------------------
// Reset hard drive(s)
//----------------------------------------------------------------------------
void ATA_SW_Reset(void)
{
	WriteBYTE(CTRL_A + CTRL_DEV_CTRL, CT_SRST | CT_nIEN); delay10us();
	WriteBYTE(CTRL_A + CTRL_DEV_CTRL, CT_nIEN); delay10us();
	CheckBusy();
}


//----------------------------------------------------------------------------
// Read one or more sectors in CSH mode from drive 0
// Returns 0 if no error detected
//---------------------------------------------------------------------------- 
u08 ATA_ReadCHS0(u08 Head, u16 Track, u08 Sector, u08 NumSectors, u08 *Buffer)
{
	cli();
	// Prepare parameters...
	CheckBusy();
  	WriteBYTE(CMD_A + CMD_DEV_HEAD, DH_CHS0 | Head);
	CheckDataReady();
  	WriteBYTE(CMD_A + CMD_CYL_HIGH, Track >> 8);
  	WriteBYTE(CMD_A + CMD_CYL_LOW, Track);
  	WriteBYTE(CMD_A + CMD_SECT_NUM, Sector);
  	WriteBYTE(CMD_A + CMD_SECT_CNT, NumSectors);

  	// Issue read sector command...
  	WriteBYTE(CMD_A + CMD_COMMAND, ATA_READ_SECTS);
	delay10us();

#ifndef DEBUG_ATA
	return ExecuteCommand(NumSectors, Buffer);
#else
	{
		u08 res = ExecuteCommand(NumSectors, Buffer);
		PRINT("CHS RD\n");
		DiskStat();
		return res;
	}
#endif
}

//----------------------------------------------------------------------------
// Read one or more sectors in LBA mode from drive 0
// Returns 0 if no error detected
//---------------------------------------------------------------------------- 
u08 ATA_ReadLBA0(u32 LBA, u08 NumSectors, u08 *Buffer)
{
	union u32convert LBAC;
	LBAC.value = LBA;

	cli();
	// Prepare parameters...
	CheckBusy();
  	WriteBYTE(CMD_A + CMD_DEV_HEAD, DH_LBA0 | LBAC.bytes.byte4); // <= 28 bits !!!
	CheckDataReady();
	WriteBYTE(CMD_A + CMD_CYL_HIGH, LBAC.bytes.byte3);
  	WriteBYTE(CMD_A + CMD_CYL_LOW,  LBAC.bytes.byte2);
  	WriteBYTE(CMD_A + CMD_SECT_NUM, LBAC.bytes.byte1);
  	WriteBYTE(CMD_A + CMD_SECT_CNT, NumSectors);

  	// Issue read sector command...
  	WriteBYTE(CMD_A + CMD_COMMAND, ATA_READ_SECTS);
	delay10us();

#ifndef DEBUG_ATA
	return ExecuteCommand(NumSectors, Buffer);
#else
	{
		u08 res = ExecuteCommand(NumSectors, Buffer);
		PRINT("LBA ");
		UART_Printfu32(LBA);
		PRINT(" ");
		UART_Printfu16(NumSectors);
		PRINT(" ");
		DiskStat();
		return res;
	}
#endif
}


#define MINIBUFFERSIZE 32
u08 minibuffer[MINIBUFFERSIZE];

//----------------------------------------------------------------------------
// Execute read command for one or more sectors. ATA command is already issued.
// Returns 0 if no error detected
//---------------------------------------------------------------------------- 
u08 ExecuteCommand(u08 NumSectors, u08 *Buffer)
{
  	register u08 i;
#ifdef ASM_OPT
	register u08 j;
	asm volatile ("mov r26,r22");
	asm volatile ("mov r27,r23");
#undef ENABLE_CRC
#endif

#ifdef ENABLE_CRC
	u16  intCRC = 0, extCRC, buf_size = NumSectors * BPS;
	u08* crc_buf = Buffer;
#endif // ENABLE_CRC
	do									// suppose that NumSectors at least 1
	{									// for each sector requested
		WDR;							// enable full timeout for watchdog
		// loop reading ALT STATUS until BUSY is cleared
		while ((ReadBYTE(CTRL_A + CTRL_ALT_STAT) & SR_BUSY) == SR_BUSY);
		// then read STATUS register to reset interrupt
		u08 err = ReadBYTE(CMD_A + CMD_STATUS);
	
		if (err & SR_ERR)				// fail on error
		{
			PRINT("CMD ERR\n");
			DiskStat();
			return err;
		}
		if (!(err & SR_DRQ))			// failure if DRQ not set
		{
			PRINT("NO DATA\n");
			DiskStat();
			return err;
		}
#ifdef ASM_OPT
		i = 0;
		do									// cycle sector by minibuffers 
		{
			outp(0x00, DDRA);				// port A as input
			outp(0x00, DDRC);				// port C as input
			outp(0xff, PORTA);				// activate Pullups
			outp(0xff, PORTC);				// activate Pullups
			SetAddress(CMD_A + CMD_DATA);	// setup addressing and chip selects
			cbi(MCUCR, SRE);				// disable ExtRAM and store data into
											//  the temporary buffer
			asm volatile ("ldi r30,lo8(minibuffer)");
			asm volatile ("ldi r31,hi8(minibuffer)");
			j = 0;
			do								// cycle minibuffer by words
			{
				cbi(PORTB, 1);				// set DIOR lo
				j += 2;						// nop replacement, allow pin change
				inp(PINA);					// read lo byte	
				asm volatile ("st Z+,r24");	// store to minibuf
				inp(PINC);					// read hi byte
				asm volatile ("st Z+,r24");	// store to minibuf
				sbi(PORTB, 1);				// set DIOR hi
			} while (j < MINIBUFFERSIZE);
			sbi(MCUCR, SRE);				// enable ExtRAM and flush temporary
											//  buffer
			asm volatile ("ldi r30,lo8(minibuffer)");
			asm volatile ("ldi r31,hi8(minibuffer)");
			j = 0;
			do
			{
				asm volatile ("ld r24,Z+");
				asm volatile ("st X+,r24");
			} while (++j < MINIBUFFERSIZE);
		} while (++i < BPS / MINIBUFFERSIZE);
///#endif // ASM_OPT
#else // ASM_OPT
///#ifdef ASM_OPT
		u08 *miniPtr;
		for (i = 0; i < BPS / MINIBUFFERSIZE; i++)
		{
			outp(0x00, DDRA);			// port A as input
			outp(0x00, DDRC);			// port C as input
			outp(0xff, PORTA);			// activate Pullups
			outp(0xff, PORTC);			// activate Pullups
			SetAddress(CMD_A + CMD_DATA); // setup addressing and chip selects

			cbi(MCUCR, SRE);			// Disable ExtRAM and store data into
										//  the temporary buffer
			for (miniPtr = minibuffer; miniPtr < &minibuffer[MINIBUFFERSIZE];)
			{	
				cbi(PORTB, 1);			// set DIOR lo
				asm volatile ("nop");	// allow pin change
#ifdef ENABLE_CRC
				err = inp(PINA);
				intCRC += err;
				*miniPtr++ = err;
				err = inp(PINC);
				intCRC += err;
				*miniPtr++ = err;
#else // ENABLE_CRC
				*miniPtr++ = inp(PINA);	// read lo byte
				*miniPtr++ = inp(PINC);	// read hi byte
#endif // ENABLE_CRC
				sbi(PORTB, 1);			// set DIOR hi
			}
			sbi(MCUCR, SRE);			// enable ExtRAM and flush temporary buffer
	
			for (miniPtr = minibuffer; miniPtr < &minibuffer[MINIBUFFERSIZE];)
				*Buffer++ = *miniPtr++;
		} // for (i < BPS / MINIBUF
#endif // ASM_OPT

	} while (--NumSectors != 0);
	sei();								// enable interrupt again

#ifdef ENABLE_CRC
	extCRC = CalculateCRC(crc_buf, buf_size);	
	if (extCRC != intCRC)
	{
		UART_Puts("Err chk ");
		UART_Printfu16(intCRC);
		UART_Printfu16(extCRC);
		UART_Puts(" ata, buf ");
		UART_Printfu16((u16)crc_buf);
		UART_Printfu16(buf_size);
		EOL();
	}
	if (crc_buf >= (u08*)BUFFERSTART)
		crcs[((u16)crc_buf - BUFFERSTART) / 0x1000] = extCRC;
#endif // ENABLE_CRC

	return 0;
}


//----------------------------------------------------------------------------
// Select address and CS signals
//
// addressing bits
// 35 DA0	A0	0x01	Address Line 0
// 33 DA1	A1	0x02	Address Line 1
// 36 DA2	A2	0x04	Address Line 2
//
// chip selects
// 37 CS0	A3 	0x08	Command Block Select
// 38 CS1	A4	0x10	Control Block Select
//
//
//----------------------------------------------------------------------------
u08 SetAddress(u08 addr)
{
//	register u16 i;
//	
//	if (cs == CTRL)
//		i = 0xE000 + adr + 0x08;		// select A4 low -> CS1 -> CTRL
//	else 
//		i = 0xE000 + adr + 0x10;		// select A3 low -> CS0 -> CMD
//
//	return *(u08 *)i;

	return *(u08 *)(0xE000 + addr);
	// dummy return to avoid optimization problems
}


//----------------------------------------------------------------------------
// Read data BYTE from Drive
//----------------------------------------------------------------------------
u08 ReadBYTE(u08 addr)
{ 
	register u08 tmp;

	SetAddress(addr);

	cbi(MCUCR, SRE);			// disable ExtRAM

	outp(0x00, DDRA);			// port A as input
	outp(0x00, DDRC);			// port C as input
	outp(0xff, PORTA);			// activate Pullups
	outp(0xff, PORTC);			// activate Pullups

	cbi(PORTB, 1);				// set DIOR lo
	asm volatile ("nop");		// allow pin change
	tmp = inp(PINA);			// read byte
	sbi(PORTB, 1);				// set DIOR hi
	sbi(MCUCR, SRE);			// enable ExtRAM

  	return tmp;
}
 
 
//----------------------------------------------------------------------------
// Write data BYTE to Drive
//----------------------------------------------------------------------------
void WriteBYTE(u08 addr, u08 dat)
{ 
  	SetAddress(addr); 

	outp(0xff, DDRA);			// port A as output
	outp(0xff, DDRC);			// port C as output

	cbi(MCUCR, SRE);			// disable ExtRAM

	asm volatile ("nop");		// allow pin change
	cbi(PORTB, 0);				// set DIOW lo
	asm volatile ("nop");		// allow pin change
	outp(dat, PORTA);			// write byte
	sbi(PORTB, 0);				// set DIOW hi
	sbi(MCUCR, SRE);			// enable ExtRAM
}
 

void CheckBusy(void)
{
	WDR;
	while ((ReadBYTE(CMD_A + CMD_STATUS) & SR_BUSY) == SR_BUSY);
}


void CheckDataReady(void)
{
	WDR;
	while ((ReadBYTE(CMD_A + CMD_STATUS) & (SR_BUSY | SR_DRDY)) != SR_DRDY);
}


#ifdef DEBUG_ATA
void DiskStat(void)
{
	register u08 b;
	sei();
	b = ReadBYTE(CMD_A + CMD_ERROR);	
	PRINT("Error : "); UART_Printfu08(b); EOL();
	if (b) 
	{
		b = ReadBYTE(CMD_A + CMD_SECT_CNT);
		PRINT("SecCnt: "); UART_Printfu08(b); EOL();
		b = ReadBYTE(CMD_A + CMD_SECT_NUM);
		PRINT("SecNbr: "); UART_Printfu08(b); EOL();
		b = ReadBYTE(CMD_A + CMD_CYL_LOW);
		PRINT("CylLow: "); UART_Printfu08(b); EOL();
		b = ReadBYTE(CMD_A + CMD_CYL_HIGH);
		PRINT("CylHi : "); UART_Printfu08(b); EOL();
		b = ReadBYTE(CMD_A + CMD_DEV_HEAD);
		PRINT("Dev/Hd: "); UART_Printfu08(b); EOL();
		b = ReadBYTE(CMD_A + CMD_STATUS);
		PRINT("Status: "); UART_Printfu08(b); EOL();
	}
}


u08 IdentifyDrive0(u08 *Buffer)
{
	// Prepare parameters...
	CheckBusy();
  	WriteBYTE(CMD_A + CMD_DEV_HEAD, DH_CHS0);
	CheckDataReady();
	// Issue Identify command...
  	WriteBYTE(CMD_A + CMD_COMMAND, ATA_IDENTIFY);
	delay10us();

	return ExecuteCommand(1, Buffer);
}

#endif // DEBUG_ATA

#ifdef ENABLE_CRC

u16 crcs[10];

u16 CalculateCRC(u08* buf, u16 size)
{
	u08* end = &buf[size];
	u16 crc = 0;

	while (buf != end) crc += *buf++;
	return crc;
}

#endif // ENABLE_CRC

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -