/* * Fool OS Simple Floppy Driver. * * coded by M.idziorek on 20th of august 2014 A.D * * tested in : bochs, qemu and virutalbox so far * * resources: * * * http://wiki.osdev.org/Floppy_Disk_Controller#DMA_Data_Transfers * * http://forum.osdev.org/viewtopic.php?f=1&t=13538 * * http://www.brokenthorn.com/Resources/OSDev20.html * * http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/floppy.cc * * http://viralpatel.net/taj/tutorial/chs_translation.php * */ #define FOOLOS_MODULE_NAME "floppy" #include "config.h" #ifdef FOOLOS_COMPILE_FLOPPY #include "lib/logger/log.h" #include "x86.h" #define FLPY_SECTORS_PER_TRACK 18 void flpydsk_send_command (uint8_t cmd); void flpydsk_wait_irq (); void flpydsk_check_int (uint32_t* st0, uint32_t* cyl); static volatile int _CurrentDrive=0; static volatile uint8_t _FloppyDiskIRQ; // temporary dma hack //! initialize DMA to use phys addr 1k-64k void flpydsk_initialize_dma () { x86_outb (0x0a,0x06); //mask dma channel 2 x86_outb (0xd8,0xff); //reset master flip-flop //changed to 0x000 (Address) x86_outb (0x04, 0x00); //low x86_outb (0x04, 0x05); //high x86_outb (0x81,0x00); //page reg x86_outb (0xd8, 0xff); //reset master flip-flop // x86_outb (0x05, 0xff); //count to 0x23ff (number of bytes in a 3.5" floppy disk track) // x86_outb (0x05, 0x23); // hey, lets do just ONE sector instead (0x200) x86_outb (0x05, 0xff); //low x86_outb (0x05, 0x01); //high x86_outb (0x80, 0); //external page register = 0 x86_outb (0x0a, 0x02); //unmask dma channel 2 log("dma",FOOLOS_LOG_INFO,"initialized"); } //! prepare the DMA for read transfer void flpydsk_dma_read () { x86_outb (0x0a, 0x06); //mask dma channel 2 x86_outb (0x0b, 0x56); //single transfer, address increment, autoinit, read, channel 2 x86_outb (0x0a, 0x02); //unmask dma channel 2 log("dma",FOOLOS_LOG_INFO,"configured for reading"); } //! prepare the DMA for write transfer void flpydsk_dma_write () { x86_outb (0x0a, 0x06); //mask dma channel 2 x86_outb (0x0b, 0x5a); //single transfer, address increment, autoinit, write, channel 2 x86_outb (0x0a, 0x02); //unmask dma channel 2 log("dma",FOOLOS_LOG_INFO,"configured for writing"); } // // enum FLPYDSK_IO { FLPYDSK_DOR = 0x3f2, FLPYDSK_MSR = 0x3f4, FLPYDSK_FIFO = 0x3f5, //data register FLPYDSK_CTRL = 0x3f7 }; enum FLPYDSK_DOR_MASK { FLPYDSK_DOR_MASK_DRIVE0 = 0, //00000000 = here for completeness sake FLPYDSK_DOR_MASK_DRIVE1 = 1, //00000001 FLPYDSK_DOR_MASK_DRIVE2 = 2, //00000010 FLPYDSK_DOR_MASK_DRIVE3 = 3, //00000011 FLPYDSK_DOR_MASK_RESET = 4, //00000100 FLPYDSK_DOR_MASK_DMA = 8, //00001000 FLPYDSK_DOR_MASK_DRIVE0_MOTOR = 16, //00010000 FLPYDSK_DOR_MASK_DRIVE1_MOTOR = 32, //00100000 FLPYDSK_DOR_MASK_DRIVE2_MOTOR = 64, //01000000 FLPYDSK_DOR_MASK_DRIVE3_MOTOR = 128 //10000000 }; enum FLPYDSK_MSR_MASK { FLPYDSK_MSR_MASK_DRIVE1_POS_MODE = 1, //00000001 FLPYDSK_MSR_MASK_DRIVE2_POS_MODE = 2, //00000010 FLPYDSK_MSR_MASK_DRIVE3_POS_MODE = 4, //00000100 FLPYDSK_MSR_MASK_DRIVE4_POS_MODE = 8, //00001000 FLPYDSK_MSR_MASK_BUSY = 16, //00010000 FLPYDSK_MSR_MASK_DMA = 32, //00100000 FLPYDSK_MSR_MASK_DATAIO = 64, //01000000 FLPYDSK_MSR_MASK_DATAREG = 128 //10000000 }; enum FLPYDSK_CMD { FDC_CMD_READ_TRACK = 2, FDC_CMD_SPECIFY = 3, FDC_CMD_CHECK_STAT = 4, FDC_CMD_WRITE_SECT = 5, FDC_CMD_READ_SECT = 6, FDC_CMD_CALIBRATE = 7, FDC_CMD_CHECK_INT = 8, FDC_CMD_WRITE_DEL_S = 9, FDC_CMD_READ_ID_S = 0xa, FDC_CMD_READ_DEL_S = 0xc, FDC_CMD_FORMAT_TRACK = 0xd, FDC_CMD_SEEK = 0xf }; enum FLPYDSK_CMD_EXT { FDC_CMD_EXT_SKIP = 0x20, //00100000 FDC_CMD_EXT_DENSITY = 0x40, //01000000 FDC_CMD_EXT_MULTITRACK = 0x80 //10000000 }; enum FLPYDSK_GAP3_LENGTH { FLPYDSK_GAP3_LENGTH_STD = 42, FLPYDSK_GAP3_LENGTH_5_14= 32, FLPYDSK_GAP3_LENGTH_3_5= 27 }; enum FLPYDSK_SECTOR_DTL { FLPYDSK_SECTOR_DTL_128 = 0, FLPYDSK_SECTOR_DTL_256 = 1, FLPYDSK_SECTOR_DTL_512 = 2, FLPYDSK_SECTOR_DTL_1024 = 4 }; void flpydsk_write_dor (uint8_t val ) { //! write the digital output register x86_outb (FLPYDSK_DOR, val); } void flpydsk_write_ccr (uint8_t val) { //! write the configuation control x86_outb (FLPYDSK_CTRL, val); } void flpydsk_motor_on() { log(FOOLOS_MODULE_NAME,FOOLOS_LOG_DEBUG,"starting motor..."); //x86_outb (FLPYDSK_DOR, FLPYDSK_DOR_MASK_DRIVE0_MOTOR | FLPYDSK_DOR_MASK_RESET); x86_outb (FLPYDSK_DOR, 0x1c); sleep(10); // wait ~ 1/3 second } void flpydsk_motor_off() { log(FOOLOS_MODULE_NAME,FOOLOS_LOG_DEBUG,"stopping motor..."); x86_outb (FLPYDSK_DOR, 0x0c); } int flpydsk_calibrate (uint32_t drive) { uint32_t st0, cyl; if (drive >= 4) { log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"ERROR!"); return -2; } //! turn on the motor flpydsk_motor_on(); int i; for (i = 0; i < 10; i++) { log(FOOLOS_MODULE_NAME,FOOLOS_LOG_DEBUG,"calibrating"); //! send command flpydsk_send_command ( FDC_CMD_CALIBRATE ); flpydsk_send_command ( drive ); flpydsk_wait_irq (); flpydsk_check_int ( &st0, &cyl); //! did we fine cylinder 0? if so, we are done if (!cyl) { if(st0 & 0xC0) { log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"calibrating FAILED!"); } // flpydsk_control_motor (false); flpydsk_motor_off(); return 0; } } log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"calibrating FAILED!"); // flpydsk_control_motor (false); flpydsk_motor_off(); return -1; } void flpydsk_disable_controller () { flpydsk_write_dor (0); } void flpydsk_enable_controller () { flpydsk_write_dor ( FLPYDSK_DOR_MASK_RESET | FLPYDSK_DOR_MASK_DMA); } uint8_t flpydsk_read_status () { //! just return main status register return x86_inb (FLPYDSK_MSR); } uint8_t flpydsk_read_data () { //! same as above function but returns data register for reading int i; for (i = 0; i < 500; i++ ) if ( flpydsk_read_status () & FLPYDSK_MSR_MASK_DATAREG ) return x86_inb (FLPYDSK_FIFO); log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"reading data FAILED!"); } void flpydsk_send_command (uint8_t cmd) { //! wait until data register is ready. We send commands to the data register int i; for (i = 0; i < 500; i++ ) if ( flpydsk_read_status () & FLPYDSK_MSR_MASK_DATAREG ) { x86_outb(FLPYDSK_FIFO, cmd); return; } log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"writing data FAILED!"); } void flpydsk_drive_data (uint32_t stepr, uint32_t loadt, uint32_t unloadt, int dma ) { uint32_t data = 0; flpydsk_send_command (FDC_CMD_SPECIFY); data = ( (stepr & 0xf) << 4) | (unloadt & 0xf); flpydsk_send_command (data); data = (loadt) << 1 | dma; flpydsk_send_command (data); } void flpydsk_reset() { uint32_t st0, cyl; //! reset the controller log(FOOLOS_MODULE_NAME,FOOLOS_LOG_DEBUG,"reset controller"); flpydsk_disable_controller (); flpydsk_enable_controller (); flpydsk_wait_irq (); //! send CHECK_INT/SENSE INTERRUPT command to all drives int i; for (i=0; i<4; i++) flpydsk_check_int (&st0,&cyl); //! transfer speed 500kb/s flpydsk_write_ccr (0); //! pass mechanical drive info. steprate=3ms, unload time=240ms, load time=16ms // flpydsk_drive_data (3,16,240,0); // flpydsk_send_command (FDC_CMD_SPECIFY); flpydsk_send_command (0xdf); /* steprate = 3ms, unload time = 240ms */ flpydsk_send_command (0x02); /* load time = 16ms, no-DMA = 0 */ //! calibrate the disk flpydsk_calibrate ( _CurrentDrive ); } void int_floppy_handler() { X86_IRQ_BEGIN _FloppyDiskIRQ=1; X86_IRQ_END } void flpydsk_wait_irq() { log(FOOLOS_MODULE_NAME,FOOLOS_LOG_FINE,"waiting for irq6"); while (1) { sleep(2); x86_int_disable(); if(_FloppyDiskIRQ==1)break; x86_int_enable(); } _FloppyDiskIRQ = 0; x86_int_enable(); log(FOOLOS_MODULE_NAME,FOOLOS_LOG_FINE,"irq6 received"); } void flpydsk_check_int (uint32_t* st0, uint32_t* cyl) { flpydsk_send_command (FDC_CMD_CHECK_INT); *st0 = flpydsk_read_data (); *cyl = flpydsk_read_data (); } void flpydsk_write_sector_imp (uint8_t head, uint8_t track, uint8_t sector) { //sector=1; //! set the DMA for read transfer flpydsk_dma_write (); //flpydsk_drive_data (13, 1, 0xf, 0); log(FOOLOS_MODULE_NAME,FOOLOS_LOG_DEBUG,"writing head/track/sector"); uint32_t st0, cyl; //! write a sector flpydsk_send_command ( FDC_CMD_WRITE_SECT | FDC_CMD_EXT_MULTITRACK | FDC_CMD_EXT_DENSITY); flpydsk_send_command ( head << 2 | _CurrentDrive ); flpydsk_send_command ( track); flpydsk_send_command ( head); // first head should match with above! flpydsk_send_command ( sector); flpydsk_send_command ( FLPYDSK_SECTOR_DTL_512 ); //flpydsk_send_command ( 1 ); // number of tracks we want flpydsk_send_command ( FLPY_SECTORS_PER_TRACK ); // number of tracks ! /* flpydsk_send_command ( ( ( sector + 1 ) >= FLPY_SECTORS_PER_TRACK ) ? FLPY_SECTORS_PER_TRACK : sector + 1 ); */ flpydsk_send_command ( FLPYDSK_GAP3_LENGTH_3_5 ); flpydsk_send_command ( 0xff ); //! wait for irq flpydsk_wait_irq (); //! read status info int j; for (j=0; j<7; j++) flpydsk_read_data (); //! let FDC know we handled interrupt flpydsk_check_int (&st0,&cyl); } void flpydsk_read_sector_imp (uint8_t head, uint8_t track, uint8_t sector) { //sector=1; //! set the DMA for read transfer flpydsk_dma_read (); //flpydsk_drive_data (13, 1, 0xf, 0); log(FOOLOS_MODULE_NAME,FOOLOS_LOG_DEBUG,"reading head/track/sector"); uint32_t st0, cyl; //! read in a sector flpydsk_send_command ( FDC_CMD_READ_SECT | FDC_CMD_EXT_MULTITRACK | FDC_CMD_EXT_SKIP | FDC_CMD_EXT_DENSITY); flpydsk_send_command ( head << 2 | _CurrentDrive ); flpydsk_send_command ( track); flpydsk_send_command ( head); flpydsk_send_command ( sector); flpydsk_send_command ( FLPYDSK_SECTOR_DTL_512 ); //flpydsk_send_command ( 1 ); // number of tracks we want flpydsk_send_command ( FLPY_SECTORS_PER_TRACK ); // number of tracks ! /* flpydsk_send_command ( ( ( sector + 1 ) >= FLPY_SECTORS_PER_TRACK ) ? FLPY_SECTORS_PER_TRACK : sector + 1 ); */ flpydsk_send_command ( FLPYDSK_GAP3_LENGTH_3_5 ); flpydsk_send_command ( 0xff ); //! wait for irq flpydsk_wait_irq (); //! read status info int j; for (j=0; j<7; j++) flpydsk_read_data (); //! let FDC know we handled interrupt flpydsk_check_int (&st0,&cyl); } int flpydsk_seek ( uint32_t cyl, uint32_t head ) { //cyl=1; //head=1; uint32_t st0, cyl0; if (_CurrentDrive >= 4) return -1; int i; for (i = 0; i < 10; i++ ) { log(FOOLOS_MODULE_NAME,FOOLOS_LOG_DEBUG,"seeking cyl: %d, head: %d",cyl,head); //! send the command flpydsk_send_command (FDC_CMD_SEEK); flpydsk_send_command ( (head << 2) | _CurrentDrive); flpydsk_send_command (cyl); //! wait for the results phase IRQ flpydsk_wait_irq (); flpydsk_check_int (&st0,&cyl0); //! found the cylinder? if ( cyl0 == cyl) return 0; } return -1; } void flpydsk_lba_to_chs (int lba,int *head,int *track,int *sector) { //cylinder // // *track = lba % ( FLPY_SECTORS_PER_TRACK * 2 ); /// *head = ( lba % FLPY_SECTORS_PER_TRACK ) % 2;//* 2 ) ) / ( FLPY_SECTORS_PER_TRACK ); //*sector = (lba % FLPY_SECTORS_PER_TRACK) +1; *head = ( lba % ( FLPY_SECTORS_PER_TRACK * 2 ) ) / ( FLPY_SECTORS_PER_TRACK ); *track = lba / ( FLPY_SECTORS_PER_TRACK * 2 ); *sector = lba % FLPY_SECTORS_PER_TRACK + 1; } uint8_t* flpydsk_read_sector (int sectorLBA) { log(FOOLOS_MODULE_NAME,FOOLOS_LOG_DEBUG,"reading sector: %d",sectorLBA); if (_CurrentDrive >= 4) return 0; //! convert LBA sector to CHS int head=0, track=0, sector=1; flpydsk_lba_to_chs (sectorLBA, &head, &track, §or); //! turn motor on and seek to track // start motor flpydsk_motor_on(); if (flpydsk_seek (track, head) != 0) return 0; //! read sector and turn motor off flpydsk_read_sector_imp (head, track, sector); // flpydsk_control_motor (false); flpydsk_motor_off(); //! warning: this is a bit hackish //return (uint8_t*) DMA_BUFFER; return (uint8_t*) 0x0; } uint8_t* flpydsk_write_sector (int sectorLBA) { log(FOOLOS_MODULE_NAME,FOOLOS_LOG_DEBUG,"writing sector: %d",sectorLBA); if (_CurrentDrive >= 4) return 0; //! convert LBA sector to CHS int head=0, track=0, sector=1; flpydsk_lba_to_chs (sectorLBA, &head, &track, §or); //! turn motor on and seek to track // start motor flpydsk_motor_on(); if (flpydsk_seek (track, head) != 0) return 0; //! read sector and turn motor off flpydsk_write_sector_imp (head, track, sector); // flpydsk_control_motor (false); flpydsk_motor_off(); //! warning: this is a bit hackish //return (uint8_t*) DMA_BUFFER; return (uint8_t*) 0x0; } void floppy_init() { // init dma flpydsk_initialize_dma (); _CurrentDrive=0; _FloppyDiskIRQ = 0; log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"driver init (wathchdog: 0x%08X)",&_FloppyDiskIRQ); flpydsk_reset (); char *dma=0x0500; log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"test reading"); flpydsk_read_sector(65); log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"finished reading"); flpydsk_reset (); flpydsk_read_sector(64); log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"finished reading"); log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"test read: %s",dma); flpydsk_reset (); // log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"test write (sector: 100)"); // flpydsk_write_sector(100); // log(FOOLOS_MODULE_NAME,FOOLOS_LOG_INFO,"finished writing"); } #endif