Implement vfs layer
[thunix.git] / kernel / fd.c
blobd37f107c92477eb15fdc926a922a4e603e54b775
1 /**
2 * thuix/kernel/fd.c
4 * The floppy driver but now seems can't work correctly
6 * Aleaxander (C) 2007-2008
8 * Aleaxander@gmail.com
10 */
12 #include <fd.h>
13 #include <timer.h>
14 #include <thunix.h>
15 #include <asm/io.h>
16 #include <asm/system.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <hexdump.h>
22 extern unsigned long count_down;
24 #define FALSE 0
25 #define TRUE 1
28 #define LOG //printk
31 #define immoutb_p(val,port) \
32 __asm__("outb %0,%1\n\tjmp 1f\n1:\tjmp 1f\n1:"::"a" ((char) (val)),"i" (port))
35 /* these are globals used by get_result() */
36 #define MAX_REPLIES 7
37 static unsigned char reply_buffer[MAX_REPLIES];
38 #define ST0 (reply_buffer[0])
39 #define ST1 (reply_buffer[1])
40 #define ST2 (reply_buffer[2])
41 #define ST3 (reply_buffer[3])
44 static struct floppy_struct {
45 unsigned int size, sector, head, track, stretch;
46 unsigned char gap,rate,spec1;
47 } floppy = {
48 2880,18,2,80,0,
49 0x1b,0x00,0xCF /* 1.44MB diskette */
53 /* Store the return vaule of get_result, we need it to do some check */
54 //static int res;
56 static int done = FALSE;
57 static int motoron = FALSE;
58 static int changed = FALSE;
59 static unsigned char sr0;
60 static unsigned char fdc_track = 255;
62 static int track;
63 static int sector;
66 * send a byte to FD_DATA register
67 * @param: byte is the byte that needed to send to the FD_DATA
68 * @return: none
70 static void send_byte(unsigned char byte)
72 volatile int msr;
73 int counter;
75 //LOG("send_byte() called ...\n");
77 for (counter = 0; counter < 1000; counter++) {
78 sleep(1); /* delay 10s */
79 msr = inb_p(FD_STATUS) & (STATUS_READY | STATUS_DIR);
80 if (msr == STATUS_READY) {
81 outb(byte,FD_DATA);
82 return ;
85 LOG("Unable to send byte to FDC\n");
88 * get *ONE* byte of results from FD_DATA register then return what
89 * it get, or retrun -1 if faile.
91 static int get_byte()
93 volatile int msr;
94 int counter;
96 //LOG("get_byte() called ...\n");
98 for (counter = 0; counter < 1000; counter ++) {
99 sleep(1); /* delay 10ms */
100 msr = inb_p(FD_STATUS) & (STATUS_DIR|STATUS_READY|STATUS_BUSY);
101 if (msr == (STATUS_DIR|STATUS_READY|STATUS_BUSY))
102 return inb_p(FD_DATA);
104 LOG("get_byte: get status times out!\n");
105 return -1;
109 * get *ALL* the results from the FD_DATA register then store
110 * it in the global fileds reply_buffer. that's the only
111 * diffrence between get_byte() and get_result().
113 * @param: none
114 * @return: the number of reply chars
117 static int get_result(void)
119 int i = 0, counter, msr;
121 //LOG("get_result() called ...\n");
123 for (counter = 0; counter < 1000; counter ++) {
124 sleep(1); /* delay 10ms */
125 msr = inb_p(FD_STATUS) & (STATUS_DIR|STATUS_READY|STATUS_BUSY);
126 //LOG("msr %d: %x\n", i, msr);
127 if (msr == STATUS_READY)
128 return i;
129 if (msr == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
130 if ( i >= MAX_REPLIES)
131 break;
132 reply_buffer[i++] = inb_p(FD_DATA);
135 LOG("get_result:get status times out!\n");
137 return -1;
142 * This waits for FDC command to complete
144 * @param: sensei
145 * @return: if successfull then returns TRUE, or FALSE
148 static int wait_fdc(int sensei)
150 int time_out;
151 count_down = 30; /* set count_down init. value to 300 ms*/
154 * As I was developing thunix on bochs, that means we don't
155 * need a hardware delay, so I make the count_down to 10 ms
157 count_down = 1;
159 /* wait for FLOPPY_INTERRUPT hander to signal command finished */
160 while (!done && count_down)
162 time_out = count_down;
163 #if 0
165 printk("time_out:%d\n",time_out);
166 printk("done: %d\n",done);
167 }while(0);
168 res = get_result(); /* get the result of the command */
169 #endif
172 * we use get_byte() but NOT get_result() here, because i don't
173 * know where the error happened. or maybe get_byte() is better
174 * than get_result(), it just come from the test
176 ST0 = get_byte();
177 ST1 = get_byte();
178 ST2 = get_byte();
179 ST3 = get_byte();
182 memset(reply_buffer, 0, sizeof(reply_buffer));
183 get_result();
184 #if 0
185 hexdump(reply_buffer, sizeof(reply_buffer));
186 #endif
188 if (sensei) {
189 /* send a "sense interrupt status" command */
190 send_byte(FD_SENSEI);
191 sr0 = get_byte();
192 fdc_track = get_byte();
195 LOG("time left: %d\t done: %d\n", time_out, done);
196 done = FALSE;
197 if (time_out == 0)
198 return FALSE;
199 else
200 return TRUE;
205 * Converts liner sector address to head/track/sector
207 * @param: sector is the liner sector we wanna convert.
208 * @param: *head, save the head number to head
209 * @param: *track, save the track number to track
210 * @param: *sector, save the sector number to sector
212 * we return all the info. by the POINTER args
215 static void lba_to_chs(int line_sector, int *head, int *track, int *sector)
217 //LOG("sector_to_hts() called ...\n");
218 *sector = line_sector % floppy.sector;
219 *sector += 1;
221 line_sector /= floppy.sector;
223 *head = line_sector % floppy.head;
224 *track = line_sector / floppy.head;
228 /* test whether the motor is on or not */
229 static inline int is_motor_on()
231 //LOG("is_motor_on() called ...\n");
232 return motoron;
237 /* Turns the motor on if not */
238 static void motor_on(void)
240 //LOG("motor_on() called ...\n");
241 if ( !is_motor_on()) {
242 //LOG("Switch on motor...\n");
243 outb_p(0x1c,FD_DOR);
244 sleep(30); /* delay 300 milliseconds for motor on */
245 motoron = TRUE;
250 /* Truns the motor off if on */
251 static void motor_off (void)
253 //LOG("motor_off() called ...\n");
254 if (is_motor_on() ) {
255 count_down = 30; /* start motor kill countdown: about 300 ms */
256 while(count_down)
258 outb_p(0x0c,FD_DOR);
259 motoron = FALSE;
264 /* recalibrate the drive */
265 static void recalibrate(void)
268 //LOG("recalibrate() called ...\n");
270 /*turn the motor on first */
271 motor_on();
273 /* send actual command bytes */
274 send_byte(FD_RECALIBRATE);
275 send_byte(0);
277 /* wait until seek finished */
278 wait_fdc(TRUE);
283 /* seek to track */
284 static int seek(int track, int head)
286 //LOG("seek() called ...\n");
289 if (track == 0) {
290 LOG("RECALIBRATE...\n");
291 recalibrate();
292 return TRUE;
296 if (fdc_track == track)
297 return TRUE; /* already there*/
299 /* send actual command bytes */
300 send_byte(FD_SEEK);
301 send_byte(head << 2);
302 send_byte(track);
304 /* wait until seek finished */
305 if ( !wait_fdc(TRUE) )
306 ;//return FALSE; /* time out */
308 //LOG("ST0: %x\t ST1: %x\n", sr0, fdc_track);
309 if ( ((sr0 & 0xF8) != 0x20) || (fdc_track != track)) {
310 LOG("Seek track#: %d failed\n", track);
311 return FALSE;
312 } else {
313 LOG("Seek track#: %d OK ...\n", track);
314 return TRUE;
322 * reset the floppy.
324 * The first thing that the driver needs to do is reset the controller.This
325 * will put it in a known state. To reset the primary floppy controller,(in C)
327 * 1.write 0x00 to the DIGITAL_OUTPUT_REG of the desired controller
328 * 2.write 0x0C to the DIGITAL_OUTPUT_REG of the desired controller
329 * 3.wait for an interrupt from the controller
330 * 4.check interrupt status (this is function 0x08 of controllers)
331 * 5.write 0x00 to the CONFIG_CONTROL_REG
332 * 6.configure the drive desired on the controller (function 0x03 of controller)
333 * 7.calibrate the drive (function 0x07 of controller)
336 static void reset( )
338 //LOG("reset() called ...\n");
340 /* stop the motor and disable IRQ/DMA */
341 outb_p(0x0c,FD_DOR);
343 /* program data rate (500K/s) */
344 outb_p(0,FD_DCR);
346 /* re-enable interrupts */
347 outb_p(0x1c,FD_DOR);
349 /* resetting triggered an interrupt - handle it */
350 done = TRUE;
351 wait_fdc(TRUE);
353 /* specify drive timings (got these off the BIOS) */
354 send_byte(FD_SPECIFY);
355 send_byte(0xdf); /* SRT = 3ms, HUT = 240ms */
356 send_byte(0x06); /* HLT = 16ms, ND = 0 */
358 recalibrate();
364 * here we will setup the DMA, then we can use it to transfer data
365 * more efficiently. For now, we just make it transfer one sector's
366 * data once.
369 static void setup_DMA(unsigned long addr, int command)
371 int cmd = (command == FD_READ) ? DMA_READ : DMA_WRITE;
372 int count = 512 - 1;
374 cli(); /* we need a safe env. */
376 immoutb_p(4|2,0x0a); /* mask DMA 2 */
378 immoutb_p(0x0,0x0c); /* clear flip flop */
380 immoutb_p(cmd,0x0b);
382 immoutb_p(addr,4); /* 8 low bits of addr */
384 addr >>= 8;
385 immoutb_p(addr,4); /* bits 8-15 of addr */
387 addr >>= 8;
388 immoutb_p(addr,0x81); /* bits 16-19 of addr */
390 immoutb_p(count & 0xff,5); /* low 8 bits of count-1 (1024-1=0x3ff) */
392 immoutb_p(count >> 8,5); /* high 8 bits of count-1 */
394 immoutb_p(0|2,10); /* activate DMA 2 */
395 sti();
400 * And now, it's time to implenent the read or write function, that's
401 * all the floppy driver mean!
403 * Read/Write one sector once.
405 static int floppy_rw(int sector, char *buf, int command)
407 int head;
408 char *dma_buffer = buf;
409 static char tmp_dma_buffer[512];
411 //LOG("TMP dma buffer: %p\n", tmp_dma_buffer);
413 lba_to_chs(sector, &head, &track, &sector);
414 LOG("head: %d \ttrack: %d \tsector: %d\n", head, track, sector);
416 /* turn it on if not */
417 motor_on();
419 if (inb_p(FD_DIR) & 0x80) {
420 changed = TRUE;
421 seek(1, head); /* clear "disk change" status */
422 recalibrate();
423 motor_off();
424 printk("floppy_rw: Disk change detected. You are going to DIE:)\n");
426 pause(); /* just put it in DIE */
429 /* move head to the right track */
430 if (!seek(track, head)) {
431 motor_off();
432 printk("floppy_rw: Error seeking to track#%d\n", track);
433 return FALSE;
436 if ((unsigned long)buf >= 0xff000) {
437 dma_buffer = tmp_dma_buffer;
438 if (command == FD_WRITE)
439 memcpy(dma_buffer, buf, 512);
442 setup_DMA((unsigned long)dma_buffer, command);
444 send_byte(command);
445 send_byte(head<<2 | 0);
446 send_byte(track);
447 send_byte(head);
448 send_byte(sector);
449 send_byte(2); /* sector size = 125 * 2^(2) */
450 send_byte(floppy.sector);
451 send_byte(0);
452 send_byte(0xFF); /* sector size(only two valid vaules, 0xff when n!=0*/
454 if (!wait_fdc(FALSE)) {
455 //LOG("wait fdc failed!\n");
456 //return 0;
458 printk("Time out, trying operation again after reset() \n");
459 reset();
460 return floppy_rw(sector, buf, command);
464 motor_off();
466 if (/*res != 7 || */(ST0 & 0xf8) || (ST1 & 0xbf) || (ST2 & 0x73) ) {
467 if (ST1 & 0x02)
468 LOG("Drive is write protected!\n");
469 else
470 LOG("floppy_rw: bad interrupt!\n");
472 return -EIO;
473 } else {
474 LOG("floppy_rw: OK\n");
475 if ((unsigned long)buf >= 0xff000 && command == FD_READ)
476 memcpy(buf, dma_buffer, 512);
477 return 0;
482 /* Read ONE sector */
483 int floppy_read(int sector, void * buf)
485 return floppy_rw(sector, buf, FD_READ);
488 /* Write ONE sector */
489 int floppy_write(int sector, void * buf)
491 return floppy_rw(sector, buf, FD_WRITE);
495 * The two following function handles multi-sectors reading
496 * and writing.
498 int floppy_reads(int sector, void *buf, unsigned int sectors)
500 int res = 0;
502 while (sectors--) {
503 res = floppy_read(sector++, buf);
504 if (res < 0)
505 break;
506 buf += 512;
509 return res;
512 int floppy_writes(int sector, void *buf, unsigned int sectors)
514 int res = 0;
516 while (sectors--) {
517 res = floppy_write(sector++, buf);
518 if (res < 0)
519 break;
520 buf += 512;
523 return res;
526 static int times = 0;
528 * The FLOPPY_INTERRUPT handler
530 * FIXME: the current do_floppy seems wouldn't be called after every
531 * interrupt. I have no idea what's wrong with it.
533 void do_floppy(void)
535 //LOG("floppy_interrupt() called ...\n");
536 times ++;
537 LOG("floppy interrupt %d times!\n",times);
538 /* signal operation finished */
539 done = TRUE;
540 outb(0x20,0x20); /* EOI */
544 * OK, finally we got our last thing to do. You are right, that's it,
545 * initialing the floppy. As you know, initialization is always easy,
546 * just set the interrupt handler and mask off the bit of corresponding
547 * interrupt.
549 void floppy_init(void)
551 set_trap_gate(0x26, floppy_interrupt);
552 outb(inb_p(0x21)&~0x40,0x21);
555 /* debug fd read */
556 void Debug(void)
558 char buf[512];
560 LOG("BUF addr: %p\n", buf);
562 floppy_read(838, buf);
563 hexdump(buf, 128);
566 /* debug fd write */
567 void Debug_wr(void)
569 char str[512 * 10] = "hello word! This is just a floppy writing test";
570 char *buf = (char *)0x800000;
571 memcpy(buf, str, sizeof(str));
573 LOG("STRING addr: %p\n", str);
575 floppy_write(0, str);
576 floppy_writes(0, buf, 10);
577 LOG("interrupts happens %d times\n", times);