Main Page   Modules   Alphabetical List   Data Structures   File List   Data Fields   Globals   Related Pages  

floppy.c

Go to the documentation of this file.
00001 /*!     \file drivers/floppy/floppy.c
00002  *      \brief Floppy disk driver.
00003  *      \author Andrea Righi <drizzt@inwind.it>
00004  *      \date Last update: 2003-11-08
00005  *      \note Copyright (&copy;) 2003 Andrea Righi
00006  */
00007 
00008 #include <const.h>
00009 
00010 #include <arch/i386.h>
00011 #include <arch/interrupt.h>
00012 #include <arch/mem.h>
00013 #include <arch/paging.h>
00014 
00015 #include <kernel/clock.h>
00016 #include <kernel/console.h>
00017 #include <kernel/dma.h>
00018 #include <kernel/task.h>
00019 
00020 #include <kernel/floppy.h>
00021 
00022 /** \ingroup Drivers
00023  *  \defgroup FloppyDriver Floppy Disk
00024  *  The floppy disk driver.
00025  *  @{
00026  */
00027 
00028 //! Motor on flag.
00029 volatile bool fdc_motor = FALSE;
00030 
00031 //! FDC timeout.
00032 volatile int fdc_timeout = 0;
00033 
00034 //! Floppy motor kill countdown.
00035 volatile int fdc_motor_countdown=0;
00036 
00037 //! FDC operation finish.
00038 volatile bool fdc_done = FALSE;
00039 
00040 //! Disk change flag.
00041 volatile bool fdc_change = FALSE;
00042 
00043 //! FDC status (result output).
00044 volatile byte fdc_status[7] = { 0 };
00045 
00046 //! Current head position.
00047 volatile byte fdc_track = 0xFF;
00048 
00049 //! Status register 0.
00050 volatile byte ST0 = 0;
00051 
00052 //! Floppy types known from the system.
00053 static floppy_struct floppy_type[] = {
00054 { 2880, 18, 2, 80, 0x54, 0x1B, 0x00, "H1440" }, // 1.44MB
00055 { 3360, 21, 2, 80, 0x0C, 0x1C, 0x00, "H1680" }  // 1.68MB
00056 };
00057 
00058 //! Current floppy geometry.
00059 byte fdc_geometry = 0;
00060 
00061 //! Floppy r/w buffer.
00062 byte *fdc_buffer;
00063 
00064 // --- Floppy Generic Functions --------------------------------------- //
00065 
00066 //! \brief Send a byte to the FDC controller.
00067 //! \param b The byte to send.
00068 void fdc_sendbyte(byte b)
00069 {
00070         int msr;
00071         int tmo;
00072 
00073         for (tmo=0; tmo<128; tmo++)
00074         {
00075                 msr = inportb(FDC_MSR);
00076                 if ((msr & 0xC0) == 0x80)
00077                 {
00078                         outportb(FDC_DATA, b);
00079                         return;
00080                 }
00081                 inportb(0x80); //delay //
00082         }
00083 }
00084 
00085 //! \brief Get a byte from the FDC controller.
00086 //! \return The byte read from the controller.
00087 //! \exception NULL If timeout is elapsed.
00088 int fdc_getbyte()
00089 {
00090         int msr;
00091         int tmo;
00092 
00093         for (tmo=0; tmo<128; tmo++)
00094         {
00095                 msr = inportb(FDC_MSR);
00096                 if ((msr & 0xD0) == 0xD0)
00097                         return(inportb(FDC_DATA));
00098                 inportb(0x80); //delay //
00099         }
00100         return(-1); // timeout //
00101 }
00102 
00103 //! \brief Turn the floppy motor on.
00104 void fdc_motor_on()
00105 {
00106         if (!fdc_motor)
00107         {
00108                 outportb(FDC_DOR, 0x1C);
00109                 delay(FDC_TIME_MOTOR_SPINUP);
00110                 fdc_motor = TRUE;
00111         }
00112         fdc_motor_countdown = -1;
00113 }
00114 
00115 //! \brief Starts the FDC motor kill countdown.
00116 void fdc_motor_off()
00117 {
00118         if (fdc_motor && (fdc_motor_countdown==-1))
00119                 fdc_motor_countdown = FDC_TIME_MOTOR_OFF/1000*HZ;
00120 }
00121 
00122 //! \brief Wait for a FDC operation.
00123 //! \param sensei
00124 //!     \li #TRUE if a "sense interrupt status" command is required;
00125 //!     \li #FALSE for a simple timeout wait.
00126 //! \return
00127 //!     \li #TRUE if the operation has successfully done;
00128 //!     \li #FALSE if the timeout has been elapsed.
00129 /**
00130  *      This routine must be invoked every time that we have to verify
00131  *      the correct completion of an operation.
00132  *      If a "sense interrupt status" command is issued, the status of
00133  *      the controller is reported into the #ST0 variable and the
00134  *      current track is stored into the #fdc_track variable.
00135  */
00136 bool fdc_wait(bool sensei)
00137 {
00138         byte i;
00139 
00140         fdc_timeout = HZ; // timeout = 2 sec //
00141 
00142         // Wait for IRQ or timeout //
00143         while((!fdc_done) && (fdc_timeout))
00144         {
00145                 enable();
00146                 idle();
00147         }
00148 
00149         // Read command result bytes //
00150         i = 0;
00151         while ((i < 7) && (inportb(FDC_MSR) & MSR_BUSY))
00152                 fdc_status[i++] = fdc_getbyte();
00153 
00154         // Send a "sense interrupt status" command //
00155         if (sensei)
00156         {
00157                 fdc_sendbyte(CMD_SENSEI);
00158                 ST0 = fdc_getbyte();
00159                 fdc_track = fdc_getbyte();
00160         }
00161 
00162         // Timed out? //
00163         if (!fdc_done)
00164         {
00165                 // Check for disk change //
00166                 if (inportb(FDC_DIR) & 0x80)
00167                         fdc_change = TRUE;
00168                 return(FALSE);
00169         }
00170         else
00171         {
00172                 fdc_done = FALSE;
00173                 return(TRUE);
00174         }
00175 }
00176 
00177 //! \brief Recalibrate the floppy drive.
00178 void fdc_recalibrate()
00179 {
00180         // Turn the motor on //
00181         fdc_motor_on();
00182 
00183         // Send recalibrate command //
00184         fdc_sendbyte(CMD_RECAL);
00185         fdc_sendbyte(0);
00186 
00187         // Wait until recalibrate command is finished //
00188         fdc_wait(TRUE);
00189 
00190         // Turn the motor off //
00191         fdc_motor_off();
00192 }
00193 
00194 //! \brief Seek a track.
00195 //! \param track The track to seek.
00196 //! \return
00197 //!     \li #TRUE on success;
00198 //!     \li #FALSE if an error occurs.
00199 bool fdc_seek(int track)
00200 {
00201         // If already threre return //
00202         if (fdc_track == track)
00203                 return(TRUE);
00204 
00205         // Turn the motor on //
00206         fdc_motor_on();
00207 
00208         // Send seek command //
00209         fdc_sendbyte(CMD_SEEK);
00210         fdc_sendbyte(0);
00211         fdc_sendbyte(track);
00212 
00213         // Wait until seek is finished //
00214         if (!fdc_wait(TRUE))
00215         {
00216                 // Timeout! //
00217                 fdc_motor_off();
00218                 return(FALSE);
00219         }
00220 
00221         // Let the head settle for 15msec //
00222         delay(15);
00223 
00224         // Turn off the motor //
00225         fdc_motor_off();
00226 
00227         // Check if seek worked //
00228         if ((ST0 != 0x20) || (fdc_track != track))
00229                 return(FALSE);
00230         else
00231                 return(TRUE);
00232 }
00233 
00234 //! \brief Reset the floppy disk controller.
00235 void fdc_reset()
00236 {
00237         // Stop the motor and disable IRQ //
00238         outportb(FDC_DOR, 0x00);
00239 
00240         // Program data rate (500K/s) //
00241         outportb(FDC_DSR, 0x00);
00242 
00243         // re-enable floppy IRQ //
00244         outportb(FDC_DOR, 0x0C);
00245 
00246         // Wait for fdc //
00247         fdc_done = TRUE;
00248         fdc_wait(TRUE);
00249 
00250         // Specify drive timings //
00251         fdc_sendbyte(CMD_SPECIFY);
00252         fdc_sendbyte(0xDF); // SRT = 3ms, HUT = 240ms
00253         fdc_sendbyte(0x02); // HLT = 4ms, ND = 0 (DMA mode selected)
00254 
00255         // Clear disk change flag //
00256         fdc_seek(1);
00257         fdc_recalibrate();
00258         fdc_change = FALSE;
00259 }
00260 
00261 //! \brief
00262 //!     Convert from a LBA (Linear Block Address) address to a
00263 //!     CHS (Cylinder, Head, Sector) coordinates.
00264 //! \param lba The LBA address.
00265 //! \param track A pointer to the track coordinate.
00266 //! \param head A pointer to the head coordinate.
00267 //! \param sector A pointer to the sector coordinate.
00268 void lba2chs(int lba, int *track, int *head, int *sector)
00269 {
00270         *track = lba / (floppy_type[fdc_geometry].spt * floppy_type[fdc_geometry].heads);
00271         *head = (lba / floppy_type[fdc_geometry].spt) % floppy_type[fdc_geometry].heads;
00272         *sector = (lba % floppy_type[fdc_geometry].spt) + 1;
00273 }
00274 
00275 //! \brief
00276 //!     Perform a read/write operation with the floppy drive using
00277 //!     the DMA (Direct Memory Access).
00278 //! \param block The LBA address of the block on the floppy.
00279 //! \param buffer
00280 //!     The address of the buffer in memory for the transfer
00281 //!     operation. It must have a size of at least #FDC_SECTOR_SIZE
00282 //!     bytes.
00283 //! \param do_read
00284 //!     \li #TRUE to perform a read operation (from disk to memory);
00285 //!     \li #FALSE to perform a write operation (from memory to disk).
00286 //! \return
00287 //!     \li #TRUE on success;
00288 //!     \li #FALSE if an error occurs.
00289 bool fdc_rw(int block, byte *buffer, bool do_read)
00290 {
00291         int track, head, sector, tries;
00292 
00293         // Translate the logical address into CHS address //
00294         lba2chs(block, &track, &head, &sector);
00295 
00296         // Spin up the motor //
00297         fdc_motor_on();
00298 
00299         if (!do_read)
00300                 // Write operation => copy data from buffer into fdc_buffer //
00301                 memcpy(fdc_buffer, buffer, FDC_SECTOR_SIZE);
00302 
00303         for(tries=0; tries<3; tries++)
00304         {
00305                 // Check for disk changed //
00306                 if (inportb(FDC_DIR) & 0x80)
00307                 {
00308                         fdc_change = TRUE;
00309                         fdc_seek(1);
00310                         fdc_recalibrate();
00311                         fdc_motor_off();
00312                         return(FALSE);
00313                 }
00314                 // Move the head to the right track //
00315                 if (!fdc_seek(track))
00316                 {
00317                         fdc_motor_off();
00318                         return(FALSE);
00319                 }
00320                 // Program data rate //
00321                 outportb(FDC_CCR, floppy_type[fdc_geometry].rate);
00322 
00323                 // Send command //
00324                 if (do_read)
00325                 {
00326                         // Read operation //
00327                         dma_xfer(2, (dword)fdc_buffer, FDC_SECTOR_SIZE, FALSE);
00328                         fdc_sendbyte(CMD_READ);
00329                 }
00330                 else
00331                 {
00332                         // Write operation //
00333                         dma_xfer(2, (dword)fdc_buffer, FDC_SECTOR_SIZE, TRUE);
00334                         fdc_sendbyte(CMD_WRITE);
00335                 }
00336                 fdc_sendbyte(head << 2);
00337                 fdc_sendbyte(track);
00338                 fdc_sendbyte(head);
00339                 fdc_sendbyte(sector);
00340                 fdc_sendbyte(2); // 512 bytes/sector //
00341                 fdc_sendbyte(floppy_type[fdc_geometry].spt);
00342                 fdc_sendbyte(floppy_type[fdc_geometry].rwgap);
00343                 fdc_sendbyte(0xFF); // DTL = unused //
00344 
00345                 // Wait for command completion //
00346                 if (!fdc_wait(FALSE))
00347                         return(FALSE); // Timed out! //
00348 
00349                 if ((fdc_status[0] & 0xC0) == 0) break; // Worked! :) //
00350 
00351                 // Try again... //
00352                 fdc_recalibrate();
00353         }
00354 
00355         // Stop the motor //
00356         fdc_motor_off();
00357 
00358         if (do_read)
00359                 // Read operation => copy data from fdc_buffer into buffer //
00360                 memcpy(buffer, fdc_buffer, FDC_SECTOR_SIZE);
00361 
00362         // Return if the R/W operation terminates successfully //
00363         return(tries != 3);
00364 }
00365 
00366 //! \brief Read some contiguous blocks from the floppy disk.
00367 //! \param block The starting block to read.
00368 //! \param buffer
00369 //!     The destination buffer in memory where the
00370 //!     blocks will be copied.
00371 //! \param count
00372 //!     How many blocks to read.
00373 //! \return
00374 //!     \li #TRUE on success;
00375 //!     \li #FALSE if an error occurs.
00376 bool fdc_read(int block, byte *buffer, uint32_t count)
00377 {
00378         register int i;
00379 
00380         for(i=0; i<count; i++)
00381                 if (!(fdc_rw(block+i, buffer+(FDC_SECTOR_SIZE*i), TRUE)))
00382                         return(FALSE);
00383         // Read operation OK! //
00384         return(TRUE);
00385 }
00386 
00387 //! \brief Write some contiguous blocks to the floppy disk.
00388 //! \param block The starting block to write.
00389 //! \param buffer
00390 //!     The address in memory from which the data will be copied.
00391 //! \param count
00392 //!     How many blocks to write.
00393 //! \return
00394 //!     \li #TRUE on success;
00395 //!     \li #FALSE if an error occurs.
00396 bool fdc_write(int block, byte *buffer, uint32_t count)
00397 {
00398         register int i;
00399 
00400         for(i=0; i<count; i++)
00401                 if (!(fdc_rw(block+i, buffer+(FDC_SECTOR_SIZE*i), FALSE)))
00402                         return(FALSE);
00403         // Write operation OK! //
00404         return(TRUE);
00405 }
00406 
00407 //! \brief Check if the floppy disk was changed.
00408 //! \return
00409 //!     \li #TRUE if the disk was changed;
00410 //!     \li #FALSE otherwise.
00411 bool fdc_is_changed()
00412 {
00413         return(fdc_change);
00414 }
00415 
00416 // --- Interrupt routines --------------------------------------------- //
00417 
00418 //! \brief
00419 //!     This is a routine called from clock_thread() at every clock
00420 //!     tick to perform the floppy motor kill countdown and to control
00421 //!     the floppy timeouts.
00422 void floppy_thread()
00423 {
00424         if (fdc_timeout > 0)
00425                 fdc_timeout--;
00426 
00427         if (fdc_motor_countdown > 0)
00428                 fdc_motor_countdown--;
00429         else if (fdc_motor && !fdc_motor_countdown)
00430         {
00431                 outportb(FDC_DOR, 0x0C);
00432                 fdc_motor = FALSE;
00433         }
00434 }
00435 
00436 /** \ingroup Handlers */
00437 //! \brief
00438 //!     This is the floppy interrupt handler routine. It is invoked
00439 //!     every time that a floppy operation successfully completes.
00440 void floppy_handler()
00441 {
00442         fdc_done = TRUE;
00443 }
00444 
00445 // --- Initialization --------------------------------------------------//
00446 
00447 //! \brief Initialize the floppy driver.
00448 //! \return
00449 //!     \li #TRUE on success;
00450 //!     \li #FALSE if an error occurs.
00451 bool init_floppy()
00452 {
00453         int v;
00454 
00455         // Install the interrupt handler routine                        //
00456         install_irq_handler( FLOPPY_IRQ, (void *)floppy_handler );
00457 
00458         // Create the FDC buffer //
00459         fdc_buffer = PHYSICAL( dma_phys_alloc(PAGE_SIZE) );
00460 
00461         // Reset the controller //
00462         fdc_reset();
00463 
00464         // Get floppy controller version //
00465         fdc_sendbyte(CMD_VERSION);
00466         v = fdc_getbyte();
00467 
00468         switch ( v )
00469         {
00470                 case 0xFF:
00471                         return( FALSE );
00472                 break;
00473 
00474                 case 0x90:
00475                         kprintf("\n\rEnhanced controller found!");
00476                         return( TRUE );
00477                 break;
00478 
00479                 default:
00480                         kprintf("\n\r8272A/765A controller found! (cmd_version=%02X)", v);
00481                         return( TRUE );
00482                 break;
00483         }
00484 }
00485 
00486 /** @} */ // end of FloppyDriver

Generated on Fri Feb 20 15:32:15 2004 for Minirighi by doxygen1.2.18