summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/fd.c69
1 files changed, 47 insertions, 22 deletions
diff --git a/kernel/fd.c b/kernel/fd.c
index 19068c4..f6563ea 100644
--- a/kernel/fd.c
+++ b/kernel/fd.c
@@ -28,6 +28,9 @@
#define NTYPES (sizeof(ftypes) / sizeof(struct ftype))
#define NFDS (sizeof(fd) / sizeof(struct fdrive))
+/* time (ms) to wait for the FDC to become ready (RQM set) */
+#define READ_TIMEOUT 2
+
/* helper macros for converting LBA into CHS */
#define C(lba, ft) \
(lba / (ft->h * ft->s))
@@ -45,6 +48,9 @@
/* TODO: seek before reading */
/* TODO: check drive ready state in floppy driver (ensure the drive door is closed before any operation) */
/* TODO: cut back on printk() usage */
+/* TODO: implement retries for operations likely to fail */
+/* TODO: eliminate potential context switches in read_sect() */
+/* TODO: test that a drive actually exists before beginning an operation */
/* TODO: implement a timeout to prevent system hangs */
#define WAITIRQ(x) ({ \
@@ -61,6 +67,15 @@ struct sense_result {
uint8_t cyl;
} __attribute__((packed));
+struct req {
+ uint16_t c;
+ uint8_t h;
+ uint8_t s;
+ struct task_struct *wait;
+ struct req *nread;
+ struct req *nseek;
+};
+
struct ftype {
uint16_t c;
uint8_t h;
@@ -72,7 +87,6 @@ struct ftype {
struct fdrive {
struct ftype *type;
- struct task_struct *seek_wait;
uint8_t cyl;
uint8_t st0;
};
@@ -93,9 +107,10 @@ static struct fdrive fd[4];
static char buffer[512] __attribute__((aligned (0x10000)));
-static struct task_struct *read_wait;
+static struct req *read_queue;
static ssize_t read_data(void *buf, size_t len) {
+ long timeout;
ssize_t c = 0;
uint8_t *p = buf;
@@ -105,17 +120,12 @@ static ssize_t read_data(void *buf, size_t len) {
if(len > SSIZE_MAX)
len = SSIZE_MAX;
- /*
- * TODO: fix this.
- * This entire routine really is garbage.
- * It doesn't use a timeout (potentially
- * hanging the entire system) and doesn't
- * handle the DIO flag properly.
- */
while(len--) {
- while(!(inb(FDMSR) & 0x80));
+ timeout = uptime() + READ_TIMEOUT;
+
+ while(!(inb(FDMSR) & 0x80) && uptime() < timeout);
- if(!(inb(FDMSR) & 0x40))
+ if(!(inb(FDMSR) & 0xC0))
break;
*p++ = inb(FDDATA);
@@ -129,9 +139,7 @@ static ssize_t read_data(void *buf, size_t len) {
while((inb(FDMSR) & 0xC0) == 0xC0)
inb(FDDATA);
- if(!c)
- return -1;
- return c;
+ return !c ? -1 : c;
}
static void write_data(void *buf, size_t len) {
@@ -237,7 +245,7 @@ static void reset(void) {
while((inb(FDMSR) & wait));
}
-int fd_read_sect(int sect) {
+int fd_read_sect(int sect, struct req *r) {
ssize_t in;
struct {
uint8_t st0;
@@ -261,14 +269,30 @@ int fd_read_sect(int sect) {
0xFF
};
- cli();
- if(read_wait)
- sleep_on(&read_wait);
- sti();
+ /* prepare a read request */
+ memset(r, 0, sizeof(struct req));
+ r->c = C(sect, fd[0].type);
+ r->h = H(sect, fd[0].type);
+ r->s = S(sect, fd[0].type);
+
+ if(read_queue) {
+ r->nread = read_queue->nread;
+ read_queue->nread = r;
+ sleep_on(&r->wait);
+ } else {
+ read_queue = r;
+ }
printk("[fd] Reading sector 0x%01x (C: 0x%01x, H: 0x%01x, S: 0x%01x)\n", sect, cmd[2], cmd[3], cmd[4]);
- WAITIRQ(write_data(cmd, sizeof(cmd)));
+ cli();
+ write_data(cmd, sizeof(cmd));
+ sti();
+ sleep_on(&r->wait);
+
+ read_queue = r->nread;
+ if(read_queue)
+ wake_up(&read_queue->wait);
in = read_data(&res, sizeof(res));
if(in == sizeof(res) && !(res.st0 & 0xC0)) {
@@ -285,7 +309,8 @@ void fd_interrupt(void) {
struct sense_result sr;
irq_fired = 1;
- wake_up(&read_wait);
+ if(read_queue)
+ wake_up(&read_queue->wait);
if((inb(FDMSR) & 0xC0) != 0x80)
return;
@@ -301,7 +326,7 @@ void fd_block_read(struct buffer *b) {
void *p = b->b_data;
for(i = SECTS(b->b_block); i < SECTS(b->b_block + 1); i++) {
- if(fd_read_sect(i)) {
+ if(fd_read_sect(i, b->b_data)) {
b->b_device = 0;
return;
}