summaryrefslogtreecommitdiff
path: root/Source/Kernel/Devices/Floppy/FloppyController.class.cpp
blob: 3e97e25f75ac5c6e8c2e99ad1a554dc7c1e01938 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#include "FloppyController.class.h"
#include "FloppyDrive.class.h"
#include <TaskManager/Task.ns.h>
#include <DeviceManager/Dev.ns.h>
#include <VFS/Part.ns.h>

using namespace Sys;	//For outb/inb

//***********************************************************
//				STATIC, FOR DMA
//***********************************************************
Mutex FloppyController::dmaMutex(false);
u8int FloppyController::dmabuff[FLOPPY_DMALEN]
	__attribute__((aligned(0x8000)));

bool FloppyController::dmaInit(u8int direction, u32int length) {
	dmaMutex.waitLock();

	union {
		u8int b[4];
		u32int l;
	} a, c;			//Address, count

	//We want the physical address of dmabuff. We simply remove 0xC0000000, because we've mapped memory linearly.
	a.l = (u32int) &dmabuff - 0xC0000000;
	c.l = (u32int) length - 1;

	if (
			(a.l >> 24) ||		//Address must be under 16mb
			(c.l >> 16) ||		//Count must be < 64k
			(((a.l & 0xFFFF) + c.l) >> 16)	//We must not cross a 64k boundary
	   ) {			//Something is wrong
		dmaMutex.unlock();
		return false;
	}

	u8int mode;
	switch (direction) {
		case FD_READ:
			mode = 0x46;
			break;
		case FD_WRITE:
			mode = 0x4A;
			break;
		default:		//Invalid direction
			dmaMutex.unlock();
			return false;
	}

	outb(0x0a, 0x06);	//Mask chan 2

	outb(0x0c, 0xff);	//Reset flip-flop
	outb(0x04, a.b[0]);	//Address low
	outb(0x04, a.b[1]);	//Address high

	outb(0x81, a.b[2]);	//External page register

	outb(0x0c, 0xff);	//Reset flip-flop
	outb(0x05, c.b[0]);	//Count low
	outb(0x05, c.b[1]);	//Count high

	outb(0x0b, mode);	//Mode

	outb(0x0a, 0x02);	//Unmask chan 2

	return true;
}

void FloppyController::dmaRelease() {
	dmaMutex.unlock();
}

//*********************************************************
//				FOR THE CONTROLLER
//*********************************************************
u32int floppyMotorTimer() {	//This will be an independant thread
	while(1) {
		Task::currentThread->sleep(1000);	//Check only every second
		Vector<Device*> floppys = Dev::findDevices("block.floppy");
		for (u32int i = 0; i < floppys.size(); i++) {
			FloppyDrive* f = (FloppyDrive*)floppys[i];
			if (f->m_motorTimeout > 0 && f->m_motorState == FS_MOTORWAIT) {
				f->m_motorTimeout--;
				if (f->m_motorTimeout == 0)
					f->killMotor();
			}
		}
	}
	return 0;
}

FloppyController::FloppyController(u32int base, u8int irq) : m_driveMutex(false) {
	m_activeDrive = 0;
	m_base = base;
	m_irq = irq;
	m_drives[0] = NULL;
	m_drives[1] = NULL;
	m_first = false;
}

void FloppyController::detect() {	//TODO : do this better
	FloppyController *fdc = new FloppyController(0x03F0, 6);	//Standard controller, IRQ6 and base port 0x03F0
	Dev::registerDevice(fdc);

	outb(0x70, 0x10);	//CMOS detect
	u8int drives = inb(0x71);

	u8int fdd0 = (drives >> 4), fdd1 = (drives & 0x0F);
	
	if (fdd0 != FT_NONE)
		Dev::registerDevice(new FloppyDrive(fdc, 0, fdd0));
	if (fdd1 != FT_NONE)
		Dev::registerDevice(new FloppyDrive(fdc, 1, fdd1));
	fdc->reset();

	Vector<Device*> fdds = Dev::findDevices("block.floppy");
	for (u32int i = 0; i < fdds.size(); i++) {
		Part::registerDevice((BlockDevice*)fdds[i]);
	}

	new Thread(floppyMotorTimer, true);
}

String FloppyController::getClass() {
	return "controller.floppy";
}

String FloppyController::getName() {
	String irq = String::number(m_irq);
	return String("Floppy controller at IRQ ") += irq;
}

void FloppyController::checkInterrupt(int *st0, int *cyl) {
	writeCmd(FC_SENSE_INTERRUPT);
	*st0 = readData();
	*cyl = readData();
}

void FloppyController::setDOR() {
	u8int dor = 0x0C;
	if (m_activeDrive == 1)
		dor |= 0x01;
	if (m_drives[0] != NULL and m_drives[0]->m_motorState != 0)
		dor |= 0x10;
	if (m_drives[1] != NULL and m_drives[1]->m_motorState != 0)
		dor |= 0x20;
	asm volatile ("cli");
	outb(m_base + FR_DOR, dor);
	if (m_first) {	//First time we set the DOR, controller initialized
		Task::currentThread->waitIRQ(m_irq);
		int st0, cyl;
		checkInterrupt(&st0, &cyl);
		m_first = false;
	}
	asm volatile ("sti");
			//PANIC("test");
}

void FloppyController::setActiveDrive(u8int drive) {
	m_driveMutex.waitLock();
	m_activeDrive = drive;
	setDOR();
}

void FloppyController::setNoActiveDrive() {
	m_driveMutex.unlock();
}

bool FloppyController::writeCmd(u8int cmd) {
	for (int i = 0; i < 600; i++) {
		if (0x80 & inb(m_base + FR_MSR)) {
			outb(m_base + FR_FIFO, cmd);
			return true;
		}
		Task::currentThread->sleep(10);
	}
	return false;
}

u8int FloppyController::readData() {
	for (int i = 0; i < 600; i++) {
		if (0x80 & inb(m_base + FR_MSR)) {
			return inb(m_base + FR_FIFO);
		}
		Task::currentThread->sleep(10);
	}
	return 0;
}

bool FloppyController::reset() {
	outb(m_base + FR_DOR, 0x00);	//Disable controller
	m_first = true;
	setNoActiveDrive();

	if (m_drives[0] != NULL) m_drives[0]->m_motorState = 0;
	if (m_drives[1] != NULL) m_drives[1]->m_motorState = 0;

	for (int i = 0; i < 2; i++) {
		if (m_drives[i] != NULL) {
			if (!m_drives[i]->setup())
				return false;
		}
	}
	return true;
}