summaryrefslogblamecommitdiff
path: root/Source/Kernel/Devices/Floppy/FloppyDrive.class.cpp
blob: 7edf24a9175c34bc460fa9f2b7822891e12e9783 (plain) (tree)
1
2
3
4
5



                                   
                        






























































































                                                                                                 
                                                          





















                                                                                                 
                                                       




























                                                  
                                                          
















































                                                                                          
                                               











                                                                                
                                                          













                                                        






















                                                                                                                                 
























                                                                                                                              
                                                  




























































                                                                                                                           
#include "FloppyDrive.class.h"
#include "FloppyController.class.h"
#include <TaskManager/Task.ns.h>
#include <DeviceManager/Time.ns.h>
#include <Core/Log.ns.h>

using namespace Sys;

FloppyDrive::FloppyDrive(FloppyController *fdc, u8int number, u8int type) {
	m_fdc = fdc;
	m_driveNumber = number;
	m_driveType = type;
	fdc->m_drives[number] = this;

	switch (m_driveType) {
		case FT_360K525:
			m_cylinders = 40;
			m_sectors = 9;
			break;
		case FT_12M525:
			m_cylinders = 80;
			m_sectors = 15;
			break;
		case FT_720K35:
			m_cylinders = 80;
			m_sectors = 9;
			break;
		case FT_288M35:
			m_cylinders = 80;
			m_sectors = 36;
			break;
		case FT_144M35:
		default:		//all unknown types are 1.44M
			m_cylinders = 80;
			m_sectors = 18;
			break;
	}
	m_buffCyl = 0xFFFF;	//Invalid cylinder
	m_buffTime = 0;
}

String FloppyDrive::getClass() {
	return "block.floppy";
}

String FloppyDrive::getName() {
	char *fdTypes[] = {
		"NONE",
		"360kB 5.25in",
		"1.2MB 5.25in",
		"720kB 3.5in",
		"1.44MB 3.5in",
		"2.88MB 3.5in" };
	String ret;
	if (m_driveNumber == 0) {
		ret = "Floppy drive #0 : ";
	} else {
		ret = "Floppy drive #1 : ";
	}
	ret += fdTypes[m_driveType];
	return ret;
}

bool FloppyDrive::setup() {
	m_fdc->setActiveDrive(m_driveNumber);

	//Set transfer rate
	u8int xfer_rate;
	if (m_driveType == FT_360K525) {
		xfer_rate = 2;
	} else if (m_driveType == FT_720K35) {
		xfer_rate = 1;
	} else {
		xfer_rate = 0;
	}
	outb(m_fdc->m_base + FR_CCR, xfer_rate);

	m_fdc->writeCmd(FC_SPECIFY);
	m_fdc->writeCmd(0xDF);
	m_fdc->writeCmd(0x02);

	bool ret = calibrate();

	m_fdc->setNoActiveDrive();

	return ret;
}

bool FloppyDrive::calibrate() {
	if (!m_fdc->m_driveMutex.locked() or m_fdc->m_activeDrive != m_driveNumber) return false;

	int st0, cyl = -1;

	if (!setMotorState(true)) return false;

	for (int i = 0; i < 10; i++) {
		asm volatile("cli");
		m_fdc->writeCmd(FC_RECALIBRATE);
		m_fdc->writeCmd(m_driveNumber);

		Task::currThread()->waitIRQ(m_fdc->m_irq);
		m_fdc->checkInterrupt(&st0, &cyl);
		asm volatile("sti");

		if (st0 & 0xC0) {
			continue;
		}
		if (!cyl) {	//Cylinder 0 reached
			setMotorState(false);
			return true;
		}
	}
	setMotorState(false);
	return false;
}

bool FloppyDrive::setMotorState(bool on) {
	if (!m_fdc->m_driveMutex.locked() or m_fdc->m_activeDrive != m_driveNumber) return false;

	if (on) {
		if (m_motorState == FS_MOTOROFF) {
			m_motorState = FS_MOTORON;
			m_fdc->setDOR();
			Task::currThread()->sleep(500);
		}
		m_motorState = FS_MOTORON;
	} else {
		m_motorState = FS_MOTORWAIT;
		m_motorTimeout = 4;
	}
	return true;
}

bool FloppyDrive::killMotor() {
	m_motorState = FS_MOTOROFF;
	m_fdc->setDOR();
	return true;
}

bool FloppyDrive::seek(u32int cyli, s32int head) {
	if (cyli >= m_cylinders) return false;
	m_fdc->setActiveDrive(m_driveNumber);

	setMotorState(true);	//Turn on motor

	int st0, cyl = -1;

	for (u32int i = 0; i < 10; i++) {
		asm volatile ("cli");
		m_fdc->writeCmd(FC_SEEK);
		m_fdc->writeCmd(head << 2);
		m_fdc->writeCmd(cyl);

		Task::currThread()->waitIRQ(m_fdc->m_irq);
		m_fdc->checkInterrupt(&st0, &cyl);
		asm volatile("sti");

		if (st0 & 0xC0) { //Error
			continue;
		}
		if (cyl == 0xFF or cyl == 0x00) {	//0xFF for bochs, 0x00 for qemu :D
			setMotorState(false);
			m_fdc->setNoActiveDrive();
			return true;
		}
		if (cyl == (int)cyli) {
			setMotorState(false);
			m_fdc->setNoActiveDrive();
			return true;
		}
	}
	setMotorState(false);
	m_fdc->setNoActiveDrive();
	return false;
}

bool FloppyDrive::doTrack(u32int cyl, u8int dir) {
	if (!seek(cyl, 0)) return false;
	if (!seek(cyl, 1)) return false;
	m_fdc->setActiveDrive(m_driveNumber);

	u8int cmd, flags = 0xC0;
	switch (dir) {
		case FD_READ:
			cmd = FC_READ_DATA | flags;
			break;
		case FD_WRITE:
			cmd = FC_WRITE_DATA | flags;
			break;
		default:
			m_fdc->setNoActiveDrive();
			return false;
	}

	for (int i = 0; i < 20; i++) {
		setMotorState(true);

		if (!FloppyController::dmaInit(dir, 0x4800)) {
			m_fdc->setNoActiveDrive();
			setMotorState(false);
			return false;
		}

		Task::currThread()->sleep(100);

		asm volatile("cli");
		m_fdc->writeCmd(cmd);
		m_fdc->writeCmd(m_driveNumber);	//Drive number & first head << 2
		m_fdc->writeCmd(cyl);	//Cylinder
		m_fdc->writeCmd(0);		//First head
		m_fdc->writeCmd(1);		//First sector
		m_fdc->writeCmd(2);
		m_fdc->writeCmd(18);	//Number of sectors
		m_fdc->writeCmd(0x1B);
		m_fdc->writeCmd(0xFF);

		Task::currThread()->waitIRQ(m_fdc->m_irq);

		u8int st0, st1, st2, rcy, rhe, rse, bps;
		st0 = m_fdc->readData();
		st1 = m_fdc->readData();
		st2 = m_fdc->readData();
		rcy = m_fdc->readData();
		rhe = m_fdc->readData();
		rse = m_fdc->readData();
		bps = m_fdc->readData();
		asm volatile("sti");

		int error = 0;
	
		if (st0 & 0xC0) error = 1;
		if (st1 & 0x80) { Log::log(KL_ERROR, "FloppyDrive.class : error while I/O : end of cyilnder."); error = 1; }
		if (st0 & 0x08) { Log::log(KL_ERROR, "FloppyDrive.class : error while I/O : drive not ready."); error = 1; }
		if (st1 & 0x20)	{ Log::log(KL_ERROR, "FloppyDrive.class : error while I/O : CRC error."); error = 1; }
		if (st1 & 0x10) { Log::log(KL_ERROR, "FloppyDrive.class : error while I/O : controller timeout."); error = 1; }
		if (st1 & 0x04) { Log::log(KL_ERROR, "FloppyDrive.class : error while I/O : no data found."); error = 1; }
		if ((st2|st1) & 0x01) { 
			Log::log(KL_ERROR, "FloppyDrive.class : error while I/O : no address mark found.");
			error=1;
		}
		if (st2 & 0x40) { Log::log(KL_ERROR, "FloppyDrive.class : error while I/O : deleted address mark."); error = 1; }
		if (st2 & 0x20) { Log::log(KL_ERROR, "FloppyDrive.class : error while I/O : CRC error in data."); error = 1; }
		if (st2 & 0x10) { Log::log(KL_ERROR, "FloppyDrive.class : error while I/O : wrong cylinder."); error = 1; }
		if (st2 & 0x04) {
			Log::log(KL_ERROR, "FloppyDrive.class : error while I/O : uPD765 sector not found.");
			error = 1;
		}
		if (st2 & 0x02) { Log::log(KL_ERROR, "FloppyDrive.class : error while I/O : bad cylinder."); error = 1; }
		if (bps != 0x2) {
			Log::log(KL_ERROR, String("FloppyDrive.class : error while I/O : wanted 512 bytes/sector, got ")
					+= String::number(1 << (bps + 7)));
			error = 1;
		}
		if (st1 & 0x02) { Log::log(KL_ERROR, "FloppyDrive.class : error while I/O : not writable."); error = 2; }

		if (!error) CMem::memcpy(m_buffer, FloppyController::dmabuff, FLOPPY_DMALEN);	//Copy data to internal buffer
		FloppyController::dmaRelease();

		if (!error) {
			setMotorState(false);
			m_fdc->setNoActiveDrive();
			return true;
		}
		if (error > 1) {
			//Not retrying
			setMotorState(false);
			m_fdc->setNoActiveDrive();
			return false;
		}
	}
	setMotorState(false);
	return false;
}

bool FloppyDrive::readOnly() {
	m_fdc->setActiveDrive(m_driveNumber);
	asm volatile("cli");
	m_fdc->writeCmd(FC_SENSE_DRIVE_STATUS);
	m_fdc->writeCmd(m_driveNumber);
	Task::currThread()->waitIRQ(m_fdc->m_irq);
	u8int st3 = m_fdc->readData();
	asm volatile("sti");

	bool ret = (st3 & 0x80 ? true : false);

	m_fdc->setNoActiveDrive();

	return ret;
}

u64int FloppyDrive::blocks() {
	return m_cylinders * m_sectors * 2;
}

bool FloppyDrive::readBlocks(u64int start, u32int count, u8int *data) {
	u32int startblock = start;
	if (count == 1) {
		u32int cylinder = (startblock / (m_sectors * 2)), offset = (startblock % (m_sectors * 2)) * 512;
		if (m_buffCyl == cylinder && m_buffTime >= Time::uptime() - 4) {
			memcpy(data, (const u8int*)(&m_buffer[offset]), 512);
			return true;
		} else {
			if (!doTrack(cylinder, FD_READ)) return false;
			m_buffCyl = cylinder;
			m_buffTime = Time::uptime();
			memcpy(data, (const u8int*)(&m_buffer[offset]), 512);
			return true;
		}
	} else {
		m_buffCyl = 0xFFFF;	//Invalid cylinder
		m_buffTime = 0;

		u32int startcylinder = (startblock / (m_sectors * 2)), stoffset = startblock % (m_sectors * 2);
		u32int endcylinder = ((startblock + count - 1) / (m_sectors * 2)), 
			   endoffset = (startblock + count - 1) % (m_sectors * 2);
		for (u32int i = startcylinder; i <= endcylinder; i++) {
			if (!doTrack(i, FD_READ)) return false;	//Read cylinder
			//Figure out what to memcpy in data
			u32int start = 0;	//Where to start memcpying in buffer
			if (i == startcylinder) start = stoffset * 512;
			u32int count = FLOPPY_DMALEN - start;	//How much we must memcpy
			if (i == endcylinder) count = (endoffset * 512) - start + 512;
			u32int where = 0;
			if (i != startcylinder) where = (i - startcylinder - 1) * (512 * 2 * m_sectors) + (stoffset * 512);
			memcpy(&data[where], (const u8int*)(&m_buffer[start]), count);
		}
	}
	return true;
}

bool FloppyDrive::writeBlocks(u64int start, u32int count, u8int *data) { //TODO
	return false;
}

u32int FloppyDrive::blockSize() {
	return 512;
}

u64int FloppyDrive::chs2lba(u32int cylinder, u32int head, u32int sector) {
	return ((u64int)cylinder * (u64int)m_cylinders) + ((u64int)head + 2ULL) + (u64int)sector - 1ULL;
}