floppy.c

Go to the documentation of this file.
00001 /* This file contains the device dependent part of the driver for the Floppy
00002  * Disk Controller (FDC) using the NEC PD765 chip.
00003  *
00004  * The file contains two entry points:
00005  *
00006  *   floppy_task:   main entry when system is brought up
00007  *
00008  * Changes:
00009  *   Sep 11, 2005   code cleanup (Andy Tanenbaum)
00010  *   Dec 01, 2004   floppy driver moved to user-space (Jorrit N. Herder)
00011  *   Sep 15, 2004   sync alarms/ local timer management  (Jorrit N. Herder)
00012  *   Aug 12, 2003   null seek no interrupt fix  (Mike Haertel)
00013  *   May 14, 2000   d-d/i rewrite  (Kees J. Bot)
00014  *   Apr 04, 1992   device dependent/independent split  (Kees J. Bot)
00015  *   Mar 27, 1992   last details on density checking  (Kees J. Bot)
00016  *   Feb 14, 1992   check drive density on opens only  (Andy Tanenbaum)
00017  *           1991   len[] / motors / reset / step rate / ...  (Bruce Evans)
00018  *   May 13, 1991   renovated the errors loop  (Don Chapman)
00019  *           1989   I/O vector to keep up with 1-1 interleave  (Bruce Evans)
00020  *   Jan 06, 1988   allow 1.44 MB diskettes  (Al Crew)
00021  *   Nov 28, 1986   better resetting for 386  (Peter Kay)
00022  *   Oct 27, 1986   fdc_results fixed for 8 MHz  (Jakob Schripsema)
00023  */
00024 
00025 #include "floppy.h"
00026 #include <timers.h>
00027 #include <ibm/diskparm.h>
00028 #include <minix/sysutil.h>
00029 #include <minix/syslib.h>
00030 
00031 /* I/O Ports used by floppy disk task. */
00032 #define DOR            0x3F2    /* motor drive control bits */
00033 #define FDC_STATUS     0x3F4    /* floppy disk controller status register */
00034 #define FDC_DATA       0x3F5    /* floppy disk controller data register */
00035 #define FDC_RATE       0x3F7    /* transfer rate register */
00036 #define DMA_ADDR       0x004    /* port for low 16 bits of DMA address */
00037 #define DMA_TOP        0x081    /* port for top 8 bits of 24-bit DMA addr */
00038 #define DMA_COUNT      0x005    /* port for DMA count (count =  bytes - 1) */
00039 #define DMA_FLIPFLOP   0x00C    /* DMA byte pointer flip-flop */
00040 #define DMA_MODE       0x00B    /* DMA mode port */
00041 #define DMA_INIT       0x00A    /* DMA init port */
00042 #define DMA_RESET_VAL  0x006
00043 
00044 #define DMA_ADDR_MASK  0xFFFFFF /* mask to verify DMA address is 24-bit */
00045 
00046 /* Status registers returned as result of operation. */
00047 #define ST0             0x00    /* status register 0 */
00048 #define ST1             0x01    /* status register 1 */
00049 #define ST2             0x02    /* status register 2 */
00050 #define ST3             0x00    /* status register 3 (return by DRIVE_SENSE) */
00051 #define ST_CYL          0x03    /* slot where controller reports cylinder */
00052 #define ST_HEAD         0x04    /* slot where controller reports head */
00053 #define ST_SEC          0x05    /* slot where controller reports sector */
00054 #define ST_PCN          0x01    /* slot where controller reports present cyl */
00055 
00056 /* Fields within the I/O ports. */
00057 /* Main status register. */
00058 #define CTL_BUSY        0x10    /* bit is set when read or write in progress */
00059 #define DIRECTION       0x40    /* bit is set when reading data reg is valid */
00060 #define MASTER          0x80    /* bit is set when data reg can be accessed */
00061 
00062 /* Digital output port (DOR). */
00063 #define MOTOR_SHIFT        4    /* high 4 bits control the motors in DOR */
00064 #define ENABLE_INT      0x0C    /* used for setting DOR port */
00065 
00066 /* ST0. */
00067 #define ST0_BITS_TRANS  0xD8    /* check 4 bits of status */
00068 #define TRANS_ST0       0x00    /* 4 bits of ST0 for READ/WRITE */
00069 #define ST0_BITS_SEEK   0xF8    /* check top 5 bits of seek status */
00070 #define SEEK_ST0        0x20    /* top 5 bits of ST0 for SEEK */
00071 
00072 /* ST1. */
00073 #define BAD_SECTOR      0x05    /* if these bits are set in ST1, recalibrate */
00074 #define WRITE_PROTECT   0x02    /* bit is set if diskette is write protected */
00075 
00076 /* ST2. */
00077 #define BAD_CYL         0x1F    /* if any of these bits are set, recalibrate */
00078 
00079 /* ST3 (not used). */
00080 #define ST3_FAULT       0x80    /* if this bit is set, drive is sick */
00081 #define ST3_WR_PROTECT  0x40    /* set when diskette is write protected */
00082 #define ST3_READY       0x20    /* set when drive is ready */
00083 
00084 /* Floppy disk controller command bytes. */
00085 #define FDC_SEEK        0x0F    /* command the drive to seek */
00086 #define FDC_READ        0xE6    /* command the drive to read */
00087 #define FDC_WRITE       0xC5    /* command the drive to write */
00088 #define FDC_SENSE       0x08    /* command the controller to tell its status */
00089 #define FDC_RECALIBRATE 0x07    /* command the drive to go to cyl 0 */
00090 #define FDC_SPECIFY     0x03    /* command the drive to accept params */
00091 #define FDC_READ_ID     0x4A    /* command the drive to read sector identity */
00092 #define FDC_FORMAT      0x4D    /* command the drive to format a track */
00093 
00094 /* DMA channel commands. */
00095 #define DMA_READ        0x46    /* DMA read opcode */
00096 #define DMA_WRITE       0x4A    /* DMA write opcode */
00097 
00098 /* Parameters for the disk drive. */
00099 #define HC_SIZE         2880    /* # sectors on largest legal disk (1.44MB) */
00100 #define NR_HEADS        0x02    /* two heads (i.e., two tracks/cylinder) */
00101 #define MAX_SECTORS       18    /* largest # sectors per track */
00102 #define DTL             0xFF    /* determines data length (sector size) */
00103 #define SPEC2           0x02    /* second parameter to SPECIFY */
00104 #define MOTOR_OFF      (3*HZ)   /* how long to wait before stopping motor */
00105 #define WAKEUP         (2*HZ)   /* timeout on I/O, FDC won't quit. */
00106 
00107 /* Error codes */
00108 #define ERR_SEEK         (-1)   /* bad seek */
00109 #define ERR_TRANSFER     (-2)   /* bad transfer */
00110 #define ERR_STATUS       (-3)   /* something wrong when getting status */
00111 #define ERR_READ_ID      (-4)   /* bad read id */
00112 #define ERR_RECALIBRATE  (-5)   /* recalibrate didn't work properly */
00113 #define ERR_DRIVE        (-6)   /* something wrong with a drive */
00114 #define ERR_WR_PROTECT   (-7)   /* diskette is write protected */
00115 #define ERR_TIMEOUT      (-8)   /* interrupt timeout */
00116 
00117 /* No retries on some errors. */
00118 #define err_no_retry(err)       ((err) <= ERR_WR_PROTECT)
00119 
00120 /* Encoding of drive type in minor device number. */
00121 #define DEV_TYPE_BITS   0x7C    /* drive type + 1, if nonzero */
00122 #define DEV_TYPE_SHIFT     2    /* right shift to normalize type bits */
00123 #define FORMAT_DEV_BIT  0x80    /* bit in minor to turn write into format */
00124 
00125 /* Miscellaneous. */
00126 #define MAX_ERRORS         6    /* how often to try rd/wt before quitting */
00127 #define MAX_RESULTS        7    /* max number of bytes controller returns */
00128 #define NR_DRIVES          2    /* maximum number of drives */
00129 #define DIVISOR          128    /* used for sector size encoding */
00130 #define SECTOR_SIZE_CODE   2    /* code to say "512" to the controller */
00131 #define TIMEOUT_MICROS   500000L        /* microseconds waiting for FDC */
00132 #define TIMEOUT_TICKS     30    /* ticks waiting for FDC */
00133 #define NT                 7    /* number of diskette/drive combinations */
00134 #define UNCALIBRATED       0    /* drive needs to be calibrated at next use */
00135 #define CALIBRATED         1    /* no calibration needed */
00136 #define BASE_SECTOR        1    /* sectors are numbered starting at 1 */
00137 #define NO_SECTOR        (-1)   /* current sector unknown */
00138 #define NO_CYL           (-1)   /* current cylinder unknown, must seek */
00139 #define NO_DENS          100    /* current media unknown */
00140 #define BSY_IDLE           0    /* busy doing nothing */
00141 #define BSY_IO             1    /* busy doing I/O */
00142 #define BSY_WAKEN          2    /* got a wakeup call */
00143 
00144 /* Seven combinations of diskette/drive are supported.
00145  *
00146  * # Diskette Drive  Sectors  Tracks   Rotation Data-rate  Comment
00147  * 0   360K    360K     9       40     300 RPM  250 kbps   Standard PC DSDD
00148  * 1   1.2M    1.2M    15       80     360 RPM  500 kbps   AT disk in AT drive
00149  * 2   360K    720K     9       40     300 RPM  250 kbps   Quad density PC
00150  * 3   720K    720K     9       80     300 RPM  250 kbps   Toshiba, et al.
00151  * 4   360K    1.2M     9       40     360 RPM  300 kbps   PC disk in AT drive
00152  * 5   720K    1.2M     9       80     360 RPM  300 kbps   Toshiba in AT drive
00153  * 6   1.44M   1.44M   18       80     300 RPM  500 kbps   PS/2, et al.
00154  *
00155  * In addition, 720K diskettes can be read in 1.44MB drives, but that does
00156  * not need a different set of parameters.  This combination uses
00157  *
00158  * 3   720K    1.44M    9       80     300 RPM  250 kbps   PS/2, et al.
00159  */
00160 PRIVATE struct density {
00161         u8_t    secpt;          /* sectors per track */
00162         u8_t    cyls;           /* tracks per side */
00163         u8_t    steps;          /* steps per cylinder (2 = double step) */
00164         u8_t    test;           /* sector to try for density test */
00165         u8_t    rate;           /* data rate (2=250, 1=300, 0=500 kbps) */
00166         u8_t    start;          /* motor start (clock ticks) */
00167         u8_t    gap;            /* gap size */
00168         u8_t    spec1;          /* first specify byte (SRT/HUT) */
00169 } fdensity[NT] = {
00170         {  9, 40, 1, 4*9, 2, 4*HZ/8, 0x2A, 0xDF },      /*  360K / 360K  */
00171         { 15, 80, 1,  14, 0, 4*HZ/8, 0x1B, 0xDF },      /*  1.2M / 1.2M  */
00172         {  9, 40, 2, 2*9, 2, 4*HZ/8, 0x2A, 0xDF },      /*  360K / 720K  */
00173         {  9, 80, 1, 4*9, 2, 6*HZ/8, 0x2A, 0xDF },      /*  720K / 720K  */
00174         {  9, 40, 2, 2*9, 1, 4*HZ/8, 0x23, 0xDF },      /*  360K / 1.2M  */
00175         {  9, 80, 1, 4*9, 1, 4*HZ/8, 0x23, 0xDF },      /*  720K / 1.2M  */
00176         { 18, 80, 1,  17, 0, 6*HZ/8, 0x1B, 0xCF },      /* 1.44M / 1.44M */
00177 };
00178 
00179 /* The following table is used with the test_sector array to recognize a
00180  * drive/floppy combination.  The sector to test has been determined by
00181  * looking at the differences in gap size, sectors/track, and double stepping.
00182  * This means that types 0 and 3 can't be told apart, only the motor start
00183  * time differs.  If a read test succeeds then the drive is limited to the
00184  * set of densities it can support to avoid unnecessary tests in the future.
00185  */
00186 
00187 #define b(d)    (1 << (d))      /* bit for density d. */
00188 
00189 PRIVATE struct test_order {
00190         u8_t    t_density;      /* floppy/drive type */
00191         u8_t    t_class;        /* limit drive to this class of densities */
00192 } test_order[NT-1] = {
00193         { 6,  b(3) | b(6) },            /* 1.44M  {720K, 1.44M} */
00194         { 1,  b(1) | b(4) | b(5) },     /* 1.2M   {1.2M, 360K, 720K} */
00195         { 3,  b(2) | b(3) | b(6) },     /* 720K   {360K, 720K, 1.44M} */
00196         { 4,  b(1) | b(4) | b(5) },     /* 360K   {1.2M, 360K, 720K} */
00197         { 5,  b(1) | b(4) | b(5) },     /* 720K   {1.2M, 360K, 720K} */
00198         { 2,  b(2) | b(3) },            /* 360K   {360K, 720K} */
00199         /* Note that type 0 is missing, type 3 can read/write it too, which is
00200          * why the type 3 parameters have been pessimized to be like type 0.
00201          */
00202 };
00203 
00204 /* Variables. */
00205 PRIVATE struct floppy {         /* main drive struct, one entry per drive */
00206   unsigned fl_curcyl;           /* current cylinder */
00207   unsigned fl_hardcyl;          /* hardware cylinder, as opposed to: */
00208   unsigned fl_cylinder;         /* cylinder number addressed */
00209   unsigned fl_sector;           /* sector addressed */
00210   unsigned fl_head;             /* head number addressed */
00211   char fl_calibration;          /* CALIBRATED or UNCALIBRATED */
00212   u8_t fl_density;              /* NO_DENS = ?, 0 = 360K; 1 = 360K/1.2M; etc.*/
00213   u8_t fl_class;                /* bitmap for possible densities */
00214   timer_t fl_tmr_stop;          /* timer to stop motor */
00215   struct device fl_geom;        /* Geometry of the drive */
00216   struct device fl_part[NR_PARTITIONS];  /* partition's base & size */
00217 } floppy[NR_DRIVES];
00218 
00219 PRIVATE int irq_hook_id;        /* id of irq hook at the kernel */
00220 PRIVATE int motor_status;       /* bitmap of current motor status */
00221 PRIVATE int need_reset;         /* set to 1 when controller must be reset */
00222 PRIVATE unsigned f_drive;       /* selected drive */
00223 PRIVATE unsigned f_device;      /* selected minor device */
00224 PRIVATE struct floppy *f_fp;    /* current drive */
00225 PRIVATE struct density *f_dp;   /* current density parameters */
00226 PRIVATE struct density *prev_dp;/* previous density parameters */
00227 PRIVATE unsigned f_sectors;     /* equal to f_dp->secpt (needed a lot) */
00228 PRIVATE u16_t f_busy;           /* BSY_IDLE, BSY_IO, BSY_WAKEN */
00229 PRIVATE struct device *f_dv;    /* device's base and size */
00230 PRIVATE struct disk_parameter_s fmt_param; /* parameters for format */
00231 PRIVATE u8_t f_results[MAX_RESULTS];/* the controller can give lots of output */
00232 
00233 /* The floppy uses various timers. These are managed by the floppy driver
00234  * itself, because only a single synchronous alarm is available per process.
00235  * Besides the 'f_tmr_timeout' timer below, the floppy structure for each
00236  * floppy disk drive contains a 'fl_tmr_stop' timer. 
00237  */
00238 PRIVATE timer_t f_tmr_timeout;          /* timer for various timeouts */
00239 PRIVATE timer_t *f_timers;              /* queue of floppy timers */
00240 PRIVATE clock_t f_next_timeout;         /* the next timeout time */
00241 FORWARD _PROTOTYPE( void f_expire_tmrs, (struct driver *dp, message *m_ptr) );
00242 FORWARD _PROTOTYPE( void f_set_timer, (timer_t *tp, clock_t delta,
00243                                                  tmr_func_t watchdog)   );
00244 FORWARD _PROTOTYPE( void stop_motor, (timer_t *tp)                      );
00245 FORWARD _PROTOTYPE( void f_timeout, (timer_t *tp)                       );
00246 
00247 FORWARD _PROTOTYPE( struct device *f_prepare, (int device)              );
00248 FORWARD _PROTOTYPE( char *f_name, (void)                                );
00249 FORWARD _PROTOTYPE( void f_cleanup, (void)                              );
00250 FORWARD _PROTOTYPE( int f_transfer, (int proc_nr, int opcode, off_t position,
00251                                         iovec_t *iov, unsigned nr_req)  );
00252 FORWARD _PROTOTYPE( int dma_setup, (int opcode)                         );
00253 FORWARD _PROTOTYPE( void start_motor, (void)                            );
00254 FORWARD _PROTOTYPE( int seek, (void)                                    );
00255 FORWARD _PROTOTYPE( int fdc_transfer, (int opcode)                      );
00256 FORWARD _PROTOTYPE( int fdc_results, (void)                             );
00257 FORWARD _PROTOTYPE( int fdc_command, (u8_t *cmd, int len)               );
00258 FORWARD _PROTOTYPE( void fdc_out, (int val)                             );
00259 FORWARD _PROTOTYPE( int recalibrate, (void)                             );
00260 FORWARD _PROTOTYPE( void f_reset, (void)                                );
00261 FORWARD _PROTOTYPE( int f_intr_wait, (void)                             );
00262 FORWARD _PROTOTYPE( int read_id, (void)                                 );
00263 FORWARD _PROTOTYPE( int f_do_open, (struct driver *dp, message *m_ptr)  );
00264 FORWARD _PROTOTYPE( void floppy_stop, (struct driver *dp, message *m_ptr));
00265 FORWARD _PROTOTYPE( int test_read, (int density)                        );
00266 FORWARD _PROTOTYPE( void f_geometry, (struct partition *entry)          );
00267 
00268 /* Entry points to this driver. */
00269 PRIVATE struct driver f_dtab = {
00270   f_name,       /* current device's name */
00271   f_do_open,    /* open or mount request, sense type of diskette */
00272   do_nop,       /* nothing on a close */
00273   do_diocntl,   /* get or set a partitions geometry */
00274   f_prepare,    /* prepare for I/O on a given minor device */
00275   f_transfer,   /* do the I/O */
00276   f_cleanup,    /* cleanup before sending reply to user process */
00277   f_geometry,   /* tell the geometry of the diskette */
00278   floppy_stop,  /* floppy cleanup on shutdown */
00279   f_expire_tmrs,/* expire all alarm timers */
00280   nop_cancel,
00281   nop_select,
00282   NULL,
00283   NULL
00284 };
00285 
00286 /*===========================================================================*
00287  *                              floppy_task                                  *
00288  *===========================================================================*/
00289 PUBLIC void main()
00290 {
00291 /* Initialize the floppy structure and the timers. */
00292 
00293   struct floppy *fp;
00294   int s;
00295 
00296   f_next_timeout = TMR_NEVER;
00297   tmr_inittimer(&f_tmr_timeout);
00298 
00299   for (fp = &floppy[0]; fp < &floppy[NR_DRIVES]; fp++) {
00300         fp->fl_curcyl = NO_CYL;
00301         fp->fl_density = NO_DENS;
00302         fp->fl_class = ~0;
00303         tmr_inittimer(&fp->fl_tmr_stop);
00304   }
00305 
00306   /* Set IRQ policy, only request notifications, do not automatically 
00307    * reenable interrupts. ID return on interrupt is the IRQ line number. 
00308    */
00309   irq_hook_id = FLOPPY_IRQ;
00310   if ((s=sys_irqsetpolicy(FLOPPY_IRQ, 0, &irq_hook_id )) != OK)
00311         panic("FLOPPY", "Couldn't set IRQ policy", s);
00312   if ((s=sys_irqenable(&irq_hook_id)) != OK)
00313         panic("FLOPPY", "Couldn't enable IRQs", s);
00314 
00315   driver_task(&f_dtab);
00316 }
00317 
00318 /*===========================================================================*
00319  *                              f_expire_tmrs                                *
00320  *===========================================================================*/
00321 PRIVATE void f_expire_tmrs(struct driver *dp, message *m_ptr)
00322 {
00323 /* A synchronous alarm message was received. Check if there are any expired 
00324  * timers. Possibly reschedule the next alarm.  
00325  */
00326   clock_t now;                          /* current time */
00327   timer_t *tp;
00328   int s;
00329 
00330   /* Get the current time to compare the timers against. */
00331   if ((s=getuptime(&now)) != OK)
00332         panic("FLOPPY","Couldn't get uptime from clock.", s);
00333 
00334   /* Scan the timers queue for expired timers. Dispatch the watchdog function
00335    * for each expired timers. FLOPPY watchdog functions are f_tmr_timeout() 
00336    * and stop_motor(). Possibly a new alarm call must be scheduled.
00337    */
00338   tmrs_exptimers(&f_timers, now, NULL);
00339   if (f_timers == NULL) {
00340         f_next_timeout = TMR_NEVER;
00341   } else {                                        /* set new sync alarm */
00342         f_next_timeout = f_timers->tmr_exp_time;
00343         if ((s=sys_setalarm(f_next_timeout, 1)) != OK)
00344                 panic("FLOPPY","Couldn't set synchronous alarm.", s);
00345   }
00346 }
00347 
00348 /*===========================================================================*
00349  *                              f_set_timer                                  *
00350  *===========================================================================*/
00351 PRIVATE void f_set_timer(tp, delta, watchdog)
00352 timer_t *tp;                            /* timer to be set */
00353 clock_t delta;                          /* in how many ticks */
00354 tmr_func_t watchdog;                    /* watchdog function to be called */
00355 {
00356   clock_t now;                          /* current time */
00357   int s;
00358 
00359   /* Get the current time. */
00360   if ((s=getuptime(&now)) != OK)
00361         panic("FLOPPY","Couldn't get uptime from clock.", s);
00362 
00363   /* Add the timer to the local timer queue. */
00364   tmrs_settimer(&f_timers, tp, now + delta, watchdog, NULL);
00365 
00366   /* Possibly reschedule an alarm call. This happens when the front of the 
00367    * timers queue was reinserted at another position, i.e., when a timer was 
00368    * reset, or when a new timer was added in front. 
00369    */
00370   if (f_timers->tmr_exp_time != f_next_timeout) {
00371         f_next_timeout = f_timers->tmr_exp_time; 
00372         if ((s=sys_setalarm(f_next_timeout, 1)) != OK)
00373                 panic("FLOPPY","Couldn't set synchronous alarm.", s);
00374   }
00375 }
00376 
00377 /*===========================================================================*
00378  *                              f_prepare                                    *
00379  *===========================================================================*/
00380 PRIVATE struct device *f_prepare(device)
00381 int device;
00382 {
00383 /* Prepare for I/O on a device. */
00384 
00385   f_device = device;
00386   f_drive = device & ~(DEV_TYPE_BITS | FORMAT_DEV_BIT);
00387   if (f_drive < 0 || f_drive >= NR_DRIVES) return(NIL_DEV);
00388 
00389   f_fp = &floppy[f_drive];
00390   f_dv = &f_fp->fl_geom;
00391   if (f_fp->fl_density < NT) {
00392         f_dp = &fdensity[f_fp->fl_density];
00393         f_sectors = f_dp->secpt;
00394         f_fp->fl_geom.dv_size = mul64u((long) (NR_HEADS * f_sectors
00395                                         * f_dp->cyls), SECTOR_SIZE);
00396   }
00397 
00398   /* A partition? */
00399   if ((device &= DEV_TYPE_BITS) >= MINOR_fd0p0)
00400         f_dv = &f_fp->fl_part[(device - MINOR_fd0p0) >> DEV_TYPE_SHIFT];
00401 
00402   return f_dv;
00403 }
00404 
00405 /*===========================================================================*
00406  *                              f_name                                       *
00407  *===========================================================================*/
00408 PRIVATE char *f_name()
00409 {
00410 /* Return a name for the current device. */
00411   static char name[] = "fd0";
00412 
00413   name[2] = '0' + f_drive;
00414   return name;
00415 }
00416 
00417 /*===========================================================================*
00418  *                              f_cleanup                                    *
00419  *===========================================================================*/
00420 PRIVATE void f_cleanup()
00421 {
00422   /* Start a timer to turn the motor off in a few seconds. */
00423   tmr_arg(&f_fp->fl_tmr_stop)->ta_int = f_drive;
00424   f_set_timer(&f_fp->fl_tmr_stop, MOTOR_OFF, stop_motor);
00425 
00426   /* Exiting the floppy driver, so forget where we are. */
00427   f_fp->fl_sector = NO_SECTOR;
00428 }
00429 
00430 /*===========================================================================*
00431  *                              f_transfer                                   *
00432  *===========================================================================*/
00433 PRIVATE int f_transfer(proc_nr, opcode, position, iov, nr_req)
00434 int proc_nr;                    /* process doing the request */
00435 int opcode;                     /* DEV_GATHER or DEV_SCATTER */
00436 off_t position;                 /* offset on device to read or write */
00437 iovec_t *iov;                   /* pointer to read or write request vector */
00438 unsigned nr_req;                /* length of request vector */
00439 {
00440   struct floppy *fp = f_fp;
00441   iovec_t *iop, *iov_end = iov + nr_req;
00442   int s, r, errors;
00443   unsigned block;       /* Seen any 32M floppies lately? */
00444   unsigned nbytes, count, chunk, sector;
00445   unsigned long dv_size = cv64ul(f_dv->dv_size);
00446   vir_bytes user_addr;
00447   vir_bytes uaddrs[MAX_SECTORS], *up;
00448   u8_t cmd[3];
00449 
00450   /* Check disk address. */
00451   if ((position & SECTOR_MASK) != 0) return(EINVAL);
00452 
00453   errors = 0;
00454   while (nr_req > 0) {
00455         /* How many bytes to transfer? */
00456         nbytes = 0;
00457         for (iop = iov; iop < iov_end; iop++) nbytes += iop->iov_size;
00458 
00459         /* Which block on disk and how close to EOF? */
00460         if (position >= dv_size) return(OK);            /* At EOF */
00461         if (position + nbytes > dv_size) nbytes = dv_size - position;
00462         block = div64u(add64ul(f_dv->dv_base, position), SECTOR_SIZE);
00463 
00464         if ((nbytes & SECTOR_MASK) != 0) return(EINVAL);
00465 
00466         /* Using a formatting device? */
00467         if (f_device & FORMAT_DEV_BIT) {
00468                 if (opcode != DEV_SCATTER) return(EIO);
00469                 if (iov->iov_size < SECTOR_SIZE + sizeof(fmt_param))
00470                         return(EINVAL);
00471 
00472                 if ((s=sys_datacopy(proc_nr, iov->iov_addr + SECTOR_SIZE,
00473                         SELF, (vir_bytes) &fmt_param, 
00474                         (phys_bytes) sizeof(fmt_param))) != OK)
00475                         panic("FLOPPY", "Sys_vircopy failed", s);
00476 
00477                 /* Check that the number of sectors in the data is reasonable,
00478                  * to avoid division by 0.  Leave checking of other data to
00479                  * the FDC.
00480                  */
00481                 if (fmt_param.sectors_per_cylinder == 0) return(EIO);
00482 
00483                 /* Only the first sector of the parameters now needed. */
00484                 iov->iov_size = nbytes = SECTOR_SIZE;
00485         }
00486 
00487         /* Only try one sector if there were errors. */
00488         if (errors > 0) nbytes = SECTOR_SIZE;
00489 
00490         /* Compute cylinder and head of the track to access. */
00491         fp->fl_cylinder = block / (NR_HEADS * f_sectors);
00492         fp->fl_hardcyl = fp->fl_cylinder * f_dp->steps;
00493         fp->fl_head = (block % (NR_HEADS * f_sectors)) / f_sectors;
00494 
00495         /* For each sector on this track compute the user address it is to
00496          * go or to come from.
00497          */
00498         for (up = uaddrs; up < uaddrs + MAX_SECTORS; up++) *up = 0;
00499         count = 0;
00500         iop = iov;
00501         sector = block % f_sectors;
00502         for (;;) {
00503                 user_addr = iop->iov_addr;
00504                 chunk = iop->iov_size;
00505                 if ((chunk & SECTOR_MASK) != 0) return(EINVAL);
00506 
00507                 while (chunk > 0) {
00508                         uaddrs[sector++] = user_addr;
00509                         chunk -= SECTOR_SIZE;
00510                         user_addr += SECTOR_SIZE;
00511                         count += SECTOR_SIZE;
00512                         if (sector == f_sectors || count == nbytes)
00513                                 goto track_set_up;
00514                 }
00515                 iop++;
00516         }
00517   track_set_up:
00518 
00519         /* First check to see if a reset is needed. */
00520         if (need_reset) f_reset();
00521 
00522         /* See if motor is running; if not, turn it on and wait. */
00523         start_motor();
00524 
00525         /* Set the stepping rate and data rate */
00526         if (f_dp != prev_dp) {
00527                 cmd[0] = FDC_SPECIFY;
00528                 cmd[1] = f_dp->spec1;
00529                 cmd[2] = SPEC2;
00530                 (void) fdc_command(cmd, 3);
00531                 if ((s=sys_outb(FDC_RATE, f_dp->rate)) != OK)
00532                         panic("FLOPPY","Sys_outb failed", s);
00533                 prev_dp = f_dp;
00534         }
00535 
00536         /* If we are going to a new cylinder, perform a seek. */
00537         r = seek();
00538 
00539         /* Avoid read_id() if we don't plan to read much. */
00540         if (fp->fl_sector == NO_SECTOR && count < (6 * SECTOR_SIZE))
00541                 fp->fl_sector = 0;
00542 
00543         for (nbytes = 0; nbytes < count; nbytes += SECTOR_SIZE) {
00544                 if (fp->fl_sector == NO_SECTOR) {
00545                         /* Find out what the current sector is.  This often
00546                          * fails right after a seek, so try it twice.
00547                          */
00548                         if (r == OK && read_id() != OK) r = read_id();
00549                 }
00550 
00551                 /* Look for the next job in uaddrs[] */
00552                 if (r == OK) {
00553                         for (;;) {
00554                                 if (fp->fl_sector >= f_sectors)
00555                                         fp->fl_sector = 0;
00556 
00557                                 up = &uaddrs[fp->fl_sector];
00558                                 if (*up != 0) break;
00559                                 fp->fl_sector++;
00560                         }
00561                 }
00562 
00563                 if (r == OK && opcode == DEV_SCATTER) {
00564                         /* Copy the user bytes to the DMA buffer. */
00565                         if ((s=sys_datacopy(proc_nr, *up,  SELF, 
00566                                 (vir_bytes) tmp_buf,
00567                                 (phys_bytes) SECTOR_SIZE)) != OK)
00568                         panic("FLOPPY", "Sys_vircopy failed", s);
00569                 }
00570 
00571                 /* Set up the DMA chip and perform the transfer. */
00572                 if (r == OK) {
00573                         if (dma_setup(opcode) != OK) {
00574                                 /* This can only fail for addresses above 16MB
00575                                  * that cannot be handled by the controller, 
00576                                  * because it uses 24-bit addressing.
00577                                  */
00578                                 return(EIO);
00579                         }
00580                         r = fdc_transfer(opcode);
00581                 }
00582 
00583                 if (r == OK && opcode == DEV_GATHER) {
00584                         /* Copy the DMA buffer to user space. */
00585                         if ((s=sys_datacopy(SELF, (vir_bytes) tmp_buf, 
00586                                 proc_nr, *up, 
00587                                 (phys_bytes) SECTOR_SIZE)) != OK)
00588                         panic("FLOPPY", "Sys_vircopy failed", s);
00589                 }
00590 
00591                 if (r != OK) {
00592                         /* Don't retry if write protected or too many errors. */
00593                         if (err_no_retry(r) || ++errors == MAX_ERRORS) {
00594                                 return(EIO);
00595                         }
00596 
00597                         /* Recalibrate if halfway. */
00598                         if (errors == MAX_ERRORS / 2)
00599                                 fp->fl_calibration = UNCALIBRATED;
00600 
00601                         nbytes = 0;
00602                         break;          /* retry */
00603                 }
00604         }
00605 
00606         /* Book the bytes successfully transferred. */
00607         position += nbytes;
00608         for (;;) {
00609                 if (nbytes < iov->iov_size) {
00610                         /* Not done with this one yet. */
00611                         iov->iov_addr += nbytes;
00612                         iov->iov_size -= nbytes;
00613                         break;
00614                 }
00615                 nbytes -= iov->iov_size;
00616                 iov->iov_addr += iov->iov_size;
00617                 iov->iov_size = 0;
00618                 if (nbytes == 0) {
00619                         /* The rest is optional, so we return to give FS a
00620                          * chance to think it over.
00621                          */
00622                         return(OK);
00623                 }
00624                 iov++;
00625                 nr_req--;
00626         }
00627   }
00628   return(OK);
00629 }
00630 
00631 /*===========================================================================*
00632  *                              dma_setup                                    *
00633  *===========================================================================*/
00634 PRIVATE int dma_setup(opcode)
00635 int opcode;                     /* DEV_GATHER or DEV_SCATTER */
00636 {
00637 /* The IBM PC can perform DMA operations by using the DMA chip.  To use it,
00638  * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address
00639  * to be read from or written to, the byte count minus 1, and a read or write
00640  * opcode.  This routine sets up the DMA chip.  Note that the chip is not
00641  * capable of doing a DMA across a 64K boundary (e.g., you can't read a
00642  * 512-byte block starting at physical address 65520).
00643  *
00644  * Warning! Also note that it's not possible to do DMA above 16 MB because 
00645  * the ISA bus uses 24-bit addresses. Addresses above 16 MB therefore will 
00646  * be interpreted modulo 16 MB, dangerously overwriting arbitrary memory. 
00647  * A check here denies the I/O if the address is out of range. 
00648  */
00649   pvb_pair_t byte_out[9];
00650   int s;
00651 
00652   /* First check the DMA memory address not to exceed maximum. */
00653   if (tmp_phys != (tmp_phys & DMA_ADDR_MASK)) {
00654         report("FLOPPY", "DMA denied because address out of range", NO_NUM);
00655         return(EIO);
00656   }
00657 
00658   /* Set up the DMA registers.  (The comment on the reset is a bit strong,
00659    * it probably only resets the floppy channel.)
00660    */
00661   pv_set(byte_out[0], DMA_INIT, DMA_RESET_VAL); /* reset the dma controller */
00662   pv_set(byte_out[1], DMA_FLIPFLOP, 0);         /* write anything to reset it */
00663   pv_set(byte_out[2], DMA_MODE, opcode == DEV_SCATTER ? DMA_WRITE : DMA_READ);
00664   pv_set(byte_out[3], DMA_ADDR, (unsigned) tmp_phys >>  0);
00665   pv_set(byte_out[4], DMA_ADDR, (unsigned) tmp_phys >>  8);
00666   pv_set(byte_out[5], DMA_TOP, (unsigned) (tmp_phys >> 16));
00667   pv_set(byte_out[6], DMA_COUNT, (((SECTOR_SIZE - 1) >> 0) & 0xff));
00668   pv_set(byte_out[7], DMA_COUNT, (SECTOR_SIZE - 1) >> 8);
00669   pv_set(byte_out[8], DMA_INIT, 2);             /* some sort of enable */
00670 
00671   if ((s=sys_voutb(byte_out, 9)) != OK)
00672         panic("FLOPPY","Sys_voutb in dma_setup() failed", s);
00673   return(OK);
00674 }
00675 
00676 /*===========================================================================*
00677  *                              start_motor                                  *
00678  *===========================================================================*/
00679 PRIVATE void start_motor()
00680 {
00681 /* Control of the floppy disk motors is a big pain.  If a motor is off, you
00682  * have to turn it on first, which takes 1/2 second.  You can't leave it on
00683  * all the time, since that would wear out the diskette.  However, if you turn
00684  * the motor off after each operation, the system performance will be awful.
00685  * The compromise used here is to leave it on for a few seconds after each
00686  * operation.  If a new operation is started in that interval, it need not be
00687  * turned on again.  If no new operation is started, a timer goes off and the
00688  * motor is turned off.  I/O port DOR has bits to control each of 4 drives.
00689  */
00690 
00691   int s, motor_bit, running;
00692   message mess;
00693 
00694   motor_bit = 1 << f_drive;             /* bit mask for this drive */
00695   running = motor_status & motor_bit;   /* nonzero if this motor is running */
00696   motor_status |= motor_bit;            /* want this drive running too */
00697 
00698   if ((s=sys_outb(DOR,
00699                 (motor_status << MOTOR_SHIFT) | ENABLE_INT | f_drive)) != OK)
00700         panic("FLOPPY","Sys_outb in start_motor() failed", s);
00701 
00702   /* If the motor was already running, we don't have to wait for it. */
00703   if (running) return;                  /* motor was already running */
00704 
00705   /* Set an alarm timer to force a timeout if the hardware does not interrupt
00706    * in time. Expect HARD_INT message, but check for SYN_ALARM timeout.
00707    */ 
00708   f_set_timer(&f_tmr_timeout, f_dp->start, f_timeout);
00709   f_busy = BSY_IO;
00710   do {
00711         receive(ANY, &mess); 
00712         if (mess.m_type == SYN_ALARM) { 
00713                 f_expire_tmrs(NULL, NULL);
00714         } else if(mess.m_type == DEV_PING) {
00715                 notify(mess.m_source);
00716         } else {
00717                 f_busy = BSY_IDLE;
00718         }
00719   } while (f_busy == BSY_IO);
00720   f_fp->fl_sector = NO_SECTOR;
00721 }
00722 
00723 /*===========================================================================*
00724  *                              stop_motor                                   *
00725  *===========================================================================*/
00726 PRIVATE void stop_motor(tp)
00727 timer_t *tp;
00728 {
00729 /* This routine is called from an alarm timer after several seconds have
00730  * elapsed with no floppy disk activity.  It turns the drive motor off.
00731  */
00732   int s;
00733   motor_status &= ~(1 << tmr_arg(tp)->ta_int);
00734   if ((s=sys_outb(DOR, (motor_status << MOTOR_SHIFT) | ENABLE_INT)) != OK)
00735         panic("FLOPPY","Sys_outb in stop_motor() failed", s);
00736 }
00737 
00738 /*===========================================================================*
00739  *                              floppy_stop                                  *
00740  *===========================================================================*/
00741 PRIVATE void floppy_stop(struct driver *dp, message *m_ptr)
00742 {
00743 /* Stop all activity and cleanly exit with the system. */
00744   int s;
00745   sigset_t sigset = m_ptr->NOTIFY_ARG;
00746   if (sigismember(&sigset, SIGTERM) || sigismember(&sigset, SIGKSTOP)) {
00747       if ((s=sys_outb(DOR, ENABLE_INT)) != OK)
00748                 panic("FLOPPY","Sys_outb in floppy_stop() failed", s);
00749       exit(0);  
00750   }
00751 }
00752 
00753 /*===========================================================================*
00754  *                              seek                                         *
00755  *===========================================================================*/
00756 PRIVATE int seek()
00757 {
00758 /* Issue a SEEK command on the indicated drive unless the arm is already
00759  * positioned on the correct cylinder.
00760  */
00761 
00762   struct floppy *fp = f_fp;
00763   int r;
00764   message mess;
00765   u8_t cmd[3];
00766 
00767   /* Are we already on the correct cylinder? */
00768   if (fp->fl_calibration == UNCALIBRATED)
00769         if (recalibrate() != OK) return(ERR_SEEK);
00770   if (fp->fl_curcyl == fp->fl_hardcyl) return(OK);
00771 
00772   /* No.  Wrong cylinder.  Issue a SEEK and wait for interrupt. */
00773   cmd[0] = FDC_SEEK;
00774   cmd[1] = (fp->fl_head << 2) | f_drive;
00775   cmd[2] = fp->fl_hardcyl;
00776   if (fdc_command(cmd, 3) != OK) return(ERR_SEEK);
00777   if (f_intr_wait() != OK) return(ERR_TIMEOUT);
00778 
00779   /* Interrupt has been received.  Check drive status. */
00780   fdc_out(FDC_SENSE);           /* probe FDC to make it return status */
00781   r = fdc_results();            /* get controller status bytes */
00782   if (r != OK || (f_results[ST0] & ST0_BITS_SEEK) != SEEK_ST0
00783                                 || f_results[ST1] != fp->fl_hardcyl) {
00784         /* seek failed, may need a recalibrate */
00785         return(ERR_SEEK);
00786   }
00787   /* Give head time to settle on a format, no retrying here! */
00788   if (f_device & FORMAT_DEV_BIT) {
00789         /* Set a synchronous alarm to force a timeout if the hardware does
00790          * not interrupt. Expect HARD_INT, but check for SYN_ALARM timeout.
00791          */ 
00792         f_set_timer(&f_tmr_timeout, HZ/30, f_timeout);
00793         f_busy = BSY_IO;
00794         do {
00795                 receive(ANY, &mess); 
00796                 if (mess.m_type == SYN_ALARM) { 
00797                         f_expire_tmrs(NULL, NULL);
00798                 } else if(mess.m_type == DEV_PING) {
00799                         notify(mess.m_source);
00800                 } else {
00801                         f_busy = BSY_IDLE;
00802                 }
00803         } while (f_busy == BSY_IO);
00804   }
00805   fp->fl_curcyl = fp->fl_hardcyl;
00806   fp->fl_sector = NO_SECTOR;
00807   return(OK);
00808 }
00809 
00810 /*===========================================================================*
00811  *                              fdc_transfer                                 *
00812  *===========================================================================*/
00813 PRIVATE int fdc_transfer(opcode)
00814 int opcode;                     /* DEV_GATHER or DEV_SCATTER */
00815 {
00816 /* The drive is now on the proper cylinder.  Read, write or format 1 block. */
00817 
00818   struct floppy *fp = f_fp;
00819   int r, s;
00820   u8_t cmd[9];
00821 
00822   /* Never attempt a transfer if the drive is uncalibrated or motor is off. */
00823   if (fp->fl_calibration == UNCALIBRATED) return(ERR_TRANSFER);
00824   if ((motor_status & (1 << f_drive)) == 0) return(ERR_TRANSFER);
00825 
00826   /* The command is issued by outputting several bytes to the controller chip.
00827    */
00828   if (f_device & FORMAT_DEV_BIT) {
00829         cmd[0] = FDC_FORMAT;
00830         cmd[1] = (fp->fl_head << 2) | f_drive;
00831         cmd[2] = fmt_param.sector_size_code;
00832         cmd[3] = fmt_param.sectors_per_cylinder;
00833         cmd[4] = fmt_param.gap_length_for_format;
00834         cmd[5] = fmt_param.fill_byte_for_format;
00835         if (fdc_command(cmd, 6) != OK) return(ERR_TRANSFER);
00836   } else {
00837         cmd[0] = opcode == DEV_SCATTER ? FDC_WRITE : FDC_READ;
00838         cmd[1] = (fp->fl_head << 2) | f_drive;
00839         cmd[2] = fp->fl_cylinder;
00840         cmd[3] = fp->fl_head;
00841         cmd[4] = BASE_SECTOR + fp->fl_sector;
00842         cmd[5] = SECTOR_SIZE_CODE;
00843         cmd[6] = f_sectors;
00844         cmd[7] = f_dp->gap;     /* sector gap */
00845         cmd[8] = DTL;           /* data length */
00846         if (fdc_command(cmd, 9) != OK) return(ERR_TRANSFER);
00847   }
00848 
00849   /* Block, waiting for disk interrupt. */
00850   if (f_intr_wait() != OK) {
00851         printf("%s: disk interrupt timed out.\n", f_name());
00852         return(ERR_TIMEOUT);
00853   }
00854 
00855   /* Get controller status and check for errors. */
00856   r = fdc_results();
00857   if (r != OK) return(r);
00858 
00859   if (f_results[ST1] & WRITE_PROTECT) {
00860         printf("%s: diskette is write protected.\n", f_name());
00861         return(ERR_WR_PROTECT);
00862   }
00863 
00864   if ((f_results[ST0] & ST0_BITS_TRANS) != TRANS_ST0) return(ERR_TRANSFER);
00865   if (f_results[ST1] | f_results[ST2]) return(ERR_TRANSFER);
00866 
00867   if (f_device & FORMAT_DEV_BIT) return(OK);
00868 
00869   /* Compare actual numbers of sectors transferred with expected number. */
00870   s =  (f_results[ST_CYL] - fp->fl_cylinder) * NR_HEADS * f_sectors;
00871   s += (f_results[ST_HEAD] - fp->fl_head) * f_sectors;
00872   s += (f_results[ST_SEC] - BASE_SECTOR - fp->fl_sector);
00873   if (s != 1) return(ERR_TRANSFER);
00874 
00875   /* This sector is next for I/O: */
00876   fp->fl_sector = f_results[ST_SEC] - BASE_SECTOR;
00877 #if 0
00878   if (processor < 386) fp->fl_sector++;         /* Old CPU can't keep up. */
00879 #endif
00880   return(OK);
00881 }
00882 
00883 /*===========================================================================*
00884  *                              fdc_results                                  *
00885  *===========================================================================*/
00886 PRIVATE int fdc_results()
00887 {
00888 /* Extract results from the controller after an operation, then allow floppy
00889  * interrupts again.
00890  */
00891 
00892   int s, result_nr;
00893   unsigned long status;
00894   clock_t t0,t1;
00895 
00896   /* Extract bytes from FDC until it says it has no more.  The loop is
00897    * really an outer loop on result_nr and an inner loop on status. 
00898    * A timeout flag alarm is set.
00899    */
00900   result_nr = 0;
00901   getuptime(&t0);
00902   do {
00903         /* Reading one byte is almost a mirror of fdc_out() - the DIRECTION
00904          * bit must be set instead of clear, but the CTL_BUSY bit destroys
00905          * the perfection of the mirror.
00906          */
00907         if ((s=sys_inb(FDC_STATUS, &status)) != OK)
00908                 panic("FLOPPY","Sys_inb in fdc_results() failed", s);
00909         status &= (MASTER | DIRECTION | CTL_BUSY);
00910         if (status == (MASTER | DIRECTION | CTL_BUSY)) {
00911                 unsigned long tmp_r;
00912                 if (result_nr >= MAX_RESULTS) break;    /* too many results */
00913                 if ((s=sys_inb(FDC_DATA, &tmp_r)) != OK)
00914                    panic("FLOPPY","Sys_inb in fdc_results() failed", s);
00915                 f_results[result_nr] = tmp_r;
00916                 result_nr ++;
00917                 continue;
00918         }
00919         if (status == MASTER) {                 /* all read */
00920                 if ((s=sys_irqenable(&irq_hook_id)) != OK)
00921                         panic("FLOPPY", "Couldn't enable IRQs", s);
00922 
00923                 return(OK);                     /* only good exit */
00924         }
00925   } while ( (s=getuptime(&t1))==OK && (t1-t0) < TIMEOUT_TICKS );
00926   if (OK!=s) printf("FLOPPY: warning, getuptime failed: %d\n", s); 
00927   need_reset = TRUE;            /* controller chip must be reset */
00928 
00929   if ((s=sys_irqenable(&irq_hook_id)) != OK)
00930         panic("FLOPPY", "Couldn't enable IRQs", s);
00931   return(ERR_STATUS);
00932 }
00933 
00934 /*===========================================================================*
00935  *                              fdc_command                                  *
00936  *===========================================================================*/
00937 PRIVATE int fdc_command(cmd, len)
00938 u8_t *cmd;              /* command bytes */
00939 int len;                /* command length */
00940 {
00941 /* Output a command to the controller. */
00942 
00943   /* Set a synchronous alarm to force a timeout if the hardware does
00944    * not interrupt. Expect HARD_INT, but check for SYN_ALARM timeout.
00945    * Note that the actual check is done by the code that issued the
00946    * fdc_command() call.
00947    */ 
00948   f_set_timer(&f_tmr_timeout, WAKEUP, f_timeout);
00949 
00950   f_busy = BSY_IO;
00951   while (len > 0) {
00952         fdc_out(*cmd++);
00953         len--;
00954   }
00955   return(need_reset ? ERR_DRIVE : OK);
00956 }
00957 
00958 /*===========================================================================*
00959  *                              fdc_out                                      *
00960  *===========================================================================*/
00961 PRIVATE void fdc_out(val)
00962 int val;                /* write this byte to floppy disk controller */
00963 {
00964 /* Output a byte to the controller.  This is not entirely trivial, since you
00965  * can only write to it when it is listening, and it decides when to listen.
00966  * If the controller refuses to listen, the FDC chip is given a hard reset.
00967  */
00968   clock_t t0, t1;
00969   int s;
00970   unsigned long status;
00971 
00972   if (need_reset) return;       /* if controller is not listening, return */
00973 
00974   /* It may take several tries to get the FDC to accept a command.  */
00975   getuptime(&t0);
00976   do {
00977         if ( (s=getuptime(&t1))==OK && (t1-t0) > TIMEOUT_TICKS ) {
00978                 if (OK!=s) printf("FLOPPY: warning, getuptime failed: %d\n", s); 
00979                 need_reset = TRUE;      /* hit it over the head */
00980                 return;
00981         }
00982         if ((s=sys_inb(FDC_STATUS, &status)) != OK)
00983                 panic("FLOPPY","Sys_inb in fdc_out() failed", s);
00984   }
00985   while ((status & (MASTER | DIRECTION)) != (MASTER | 0)); 
00986   
00987   if ((s=sys_outb(FDC_DATA, val)) != OK)
00988         panic("FLOPPY","Sys_outb in fdc_out() failed", s);
00989 }
00990 
00991 /*===========================================================================*
00992  *                              recalibrate                                  *
00993  *===========================================================================*/
00994 PRIVATE int recalibrate()
00995 {
00996 /* The floppy disk controller has no way of determining its absolute arm
00997  * position (cylinder).  Instead, it steps the arm a cylinder at a time and
00998  * keeps track of where it thinks it is (in software).  However, after a
00999  * SEEK, the hardware reads information from the diskette telling where the
01000  * arm actually is.  If the arm is in the wrong place, a recalibration is done,
01001  * which forces the arm to cylinder 0.  This way the controller can get back
01002  * into sync with reality.
01003  */
01004 
01005   struct floppy *fp = f_fp;
01006   int r;
01007   u8_t cmd[2];
01008 
01009   /* Issue the RECALIBRATE command and wait for the interrupt. */
01010   cmd[0] = FDC_RECALIBRATE;     /* tell drive to recalibrate itself */
01011   cmd[1] = f_drive;             /* specify drive */
01012   if (fdc_command(cmd, 2) != OK) return(ERR_SEEK);
01013   if (f_intr_wait() != OK) return(ERR_TIMEOUT);
01014 
01015   /* Determine if the recalibration succeeded. */
01016   fdc_out(FDC_SENSE);           /* issue SENSE command to request results */
01017   r = fdc_results();            /* get results of the FDC_RECALIBRATE command*/
01018   fp->fl_curcyl = NO_CYL;       /* force a SEEK next time */
01019   fp->fl_sector = NO_SECTOR;
01020   if (r != OK ||                /* controller would not respond */
01021      (f_results[ST0] & ST0_BITS_SEEK) != SEEK_ST0 || f_results[ST_PCN] != 0) {
01022         /* Recalibration failed.  FDC must be reset. */
01023         need_reset = TRUE;
01024         return(ERR_RECALIBRATE);
01025   } else {
01026         /* Recalibration succeeded. */
01027         fp->fl_calibration = CALIBRATED;
01028         fp->fl_curcyl = f_results[ST_PCN];
01029         return(OK);
01030   }
01031 }
01032 
01033 /*===========================================================================*
01034  *                              f_reset                                      *
01035  *===========================================================================*/
01036 PRIVATE void f_reset()
01037 {
01038 /* Issue a reset to the controller.  This is done after any catastrophe,
01039  * like the controller refusing to respond.
01040  */
01041   pvb_pair_t byte_out[2];
01042   int s,i;
01043   message mess;
01044 
01045   /* Disable interrupts and strobe reset bit low. */
01046   need_reset = FALSE;
01047 
01048   /* It is not clear why the next lock is needed.  Writing 0 to DOR causes
01049    * interrupt, while the PC documentation says turning bit 8 off disables
01050    * interrupts.  Without the lock:
01051    *   1) the interrupt handler sets the floppy mask bit in the 8259.
01052    *   2) writing ENABLE_INT to DOR causes the FDC to assert the interrupt
01053    *      line again, but the mask stops the cpu being interrupted.
01054    *   3) the sense interrupt clears the interrupt (not clear which one).
01055    * and for some reason the reset does not work.
01056    */
01057   (void) fdc_command((u8_t *) 0, 0);   /* need only the timer */
01058   motor_status = 0;
01059   pv_set(byte_out[0], DOR, 0);                  /* strobe reset bit low */
01060   pv_set(byte_out[1], DOR, ENABLE_INT);         /* strobe it high again */
01061   if ((s=sys_voutb(byte_out, 2)) != OK)
01062         panic("FLOPPY", "Sys_voutb in f_reset() failed", s); 
01063 
01064   /* A synchronous alarm timer was set in fdc_command. Expect a HARD_INT
01065    * message to collect the reset interrupt, but be prepared to handle the 
01066    * SYN_ALARM message on a timeout.
01067    */
01068   do {
01069         receive(ANY, &mess); 
01070         if (mess.m_type == SYN_ALARM) { 
01071                 f_expire_tmrs(NULL, NULL);
01072         } else if(mess.m_type == DEV_PING) {
01073                 notify(mess.m_source);
01074         } else {                        /* expect HARD_INT */
01075                 f_busy = BSY_IDLE;
01076         }
01077   } while (f_busy == BSY_IO);
01078 
01079   /* The controller supports 4 drives and returns a result for each of them.
01080    * Collect all the results now.  The old version only collected the first
01081    * result.  This happens to work for 2 drives, but it doesn't work for 3
01082    * or more drives, at least with only drives 0 and 2 actually connected
01083    * (the controller generates an extra interrupt for the middle drive when
01084    * drive 2 is accessed and the driver panics).
01085    *
01086    * It would be better to keep collecting results until there are no more.
01087    * For this, fdc_results needs to return the number of results (instead
01088    * of OK) when it succeeds.
01089    */
01090   for (i = 0; i < 4; i++) {
01091         fdc_out(FDC_SENSE);     /* probe FDC to make it return status */
01092         (void) fdc_results();   /* flush controller */
01093   }
01094   for (i = 0; i < NR_DRIVES; i++)       /* clear each drive */
01095         floppy[i].fl_calibration = UNCALIBRATED;
01096 
01097   /* The current timing parameters must be specified again. */
01098   prev_dp = NULL;
01099 }
01100 
01101 /*===========================================================================*
01102  *                              f_intr_wait                                  *
01103  *===========================================================================*/
01104 PRIVATE int f_intr_wait()
01105 {
01106 /* Wait for an interrupt, but not forever.  The FDC may have all the time of
01107  * the world, but we humans do not.
01108  */
01109   message mess;
01110 
01111   /* We expect a HARD_INT message from the interrupt handler, but if there is
01112    * a timeout, a SYN_ALARM notification is received instead. If a timeout 
01113    * occurs, report an error.
01114    */
01115   do {
01116         receive(ANY, &mess); 
01117         if (mess.m_type == SYN_ALARM) {
01118                 f_expire_tmrs(NULL, NULL);
01119         } else if(mess.m_type == DEV_PING) {
01120                 notify(mess.m_source);
01121         } else { 
01122                 f_busy = BSY_IDLE;
01123         }
01124   } while (f_busy == BSY_IO);
01125 
01126   if (f_busy == BSY_WAKEN) {
01127 
01128         /* No interrupt from the FDC, this means that there is probably no
01129          * floppy in the drive.  Get the FDC down to earth and return error.
01130          */
01131         need_reset = TRUE;
01132         return(ERR_TIMEOUT);
01133   }
01134   return(OK);
01135 }
01136 
01137 /*===========================================================================*
01138  *                              f_timeout                                    *
01139  *===========================================================================*/
01140 PRIVATE void f_timeout(tp)
01141 timer_t *tp;
01142 {
01143 /* This routine is called when a timer expires.  Usually to tell that a
01144  * motor has spun up, but also to forge an interrupt when it takes too long
01145  * for the FDC to interrupt (no floppy in the drive).  It sets a flag to tell
01146  * what has happened.
01147  */
01148   if (f_busy == BSY_IO) {
01149         f_busy = BSY_WAKEN;
01150   }
01151 }
01152 
01153 /*===========================================================================*
01154  *                              read_id                                      *
01155  *===========================================================================*/
01156 PRIVATE int read_id()
01157 {
01158 /* Determine current cylinder and sector. */
01159 
01160   struct floppy *fp = f_fp;
01161   int result;
01162   u8_t cmd[2];
01163 
01164   /* Never attempt a read id if the drive is uncalibrated or motor is off. */
01165   if (fp->fl_calibration == UNCALIBRATED) return(ERR_READ_ID);
01166   if ((motor_status & (1 << f_drive)) == 0) return(ERR_READ_ID);
01167 
01168   /* The command is issued by outputting 2 bytes to the controller chip. */
01169   cmd[0] = FDC_READ_ID;         /* issue the read id command */
01170   cmd[1] = (fp->fl_head << 2) | f_drive;
01171   if (fdc_command(cmd, 2) != OK) return(ERR_READ_ID);
01172   if (f_intr_wait() != OK) return(ERR_TIMEOUT);
01173 
01174   /* Get controller status and check for errors. */
01175   result = fdc_results();
01176   if (result != OK) return(result);
01177 
01178   if ((f_results[ST0] & ST0_BITS_TRANS) != TRANS_ST0) return(ERR_READ_ID);
01179   if (f_results[ST1] | f_results[ST2]) return(ERR_READ_ID);
01180 
01181   /* The next sector is next for I/O: */
01182   fp->fl_sector = f_results[ST_SEC] - BASE_SECTOR + 1;
01183   return(OK);
01184 }
01185 
01186 /*===========================================================================*
01187  *                              f_do_open                                    *
01188  *===========================================================================*/
01189 PRIVATE int f_do_open(dp, m_ptr)
01190 struct driver *dp;
01191 message *m_ptr;                 /* pointer to open message */
01192 {
01193 /* Handle an open on a floppy.  Determine diskette type if need be. */
01194 
01195   int dtype;
01196   struct test_order *top;
01197 
01198   /* Decode the message parameters. */
01199   if (f_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
01200 
01201   dtype = f_device & DEV_TYPE_BITS;     /* get density from minor dev */
01202   if (dtype >= MINOR_fd0p0) dtype = 0;
01203 
01204   if (dtype != 0) {
01205         /* All types except 0 indicate a specific drive/medium combination.*/
01206         dtype = (dtype >> DEV_TYPE_SHIFT) - 1;
01207         if (dtype >= NT) return(ENXIO);
01208         f_fp->fl_density = dtype;
01209         (void) f_prepare(f_device);     /* Recompute parameters. */
01210         return(OK);
01211   }
01212   if (f_device & FORMAT_DEV_BIT) return(EIO);   /* Can't format /dev/fdN */
01213 
01214   /* The device opened is /dev/fdN.  Experimentally determine drive/medium.
01215    * First check fl_density.  If it is not NO_DENS, the drive has been used
01216    * before and the value of fl_density tells what was found last time. Try
01217    * that first.  If the motor is still running then assume nothing changed.
01218    */
01219   if (f_fp->fl_density != NO_DENS) {
01220         if (motor_status & (1 << f_drive)) return(OK);
01221         if (test_read(f_fp->fl_density) == OK) return(OK);
01222   }
01223 
01224   /* Either drive type is unknown or a different diskette is now present.
01225    * Use test_order to try them one by one.
01226    */
01227   for (top = &test_order[0]; top < &test_order[NT-1]; top++) {
01228         dtype = top->t_density;
01229 
01230         /* Skip densities that have been proven to be impossible */
01231         if (!(f_fp->fl_class & (1 << dtype))) continue;
01232 
01233         if (test_read(dtype) == OK) {
01234                 /* The test succeeded, use this knowledge to limit the
01235                  * drive class to match the density just read.
01236                  */
01237                 f_fp->fl_class &= top->t_class;
01238                 return(OK);
01239         }
01240         /* Test failed, wrong density or did it time out? */
01241         if (f_busy == BSY_WAKEN) break;
01242   }
01243   f_fp->fl_density = NO_DENS;
01244   return(EIO);                  /* nothing worked */
01245 }
01246 
01247 /*===========================================================================*
01248  *                              test_read                                    *
01249  *===========================================================================*/
01250 PRIVATE int test_read(density)
01251 int density;
01252 {
01253 /* Try to read the highest numbered sector on cylinder 2.  Not all floppy
01254  * types have as many sectors per track, and trying cylinder 2 finds the
01255  * ones that need double stepping.
01256  */
01257   int device;
01258   off_t position;
01259   iovec_t iovec1;
01260   int result;
01261 
01262   f_fp->fl_density = density;
01263   device = ((density + 1) << DEV_TYPE_SHIFT) + f_drive;
01264 
01265   (void) f_prepare(device);
01266   position = (off_t) f_dp->test << SECTOR_SHIFT;
01267   iovec1.iov_addr = (vir_bytes) tmp_buf;
01268   iovec1.iov_size = SECTOR_SIZE;
01269   result = f_transfer(SELF, DEV_GATHER, position, &iovec1, 1);
01270 
01271   if (iovec1.iov_size != 0) return(EIO);
01272 
01273   partition(&f_dtab, f_drive, P_FLOPPY, 0);
01274   return(OK);
01275 }
01276 
01277 /*===========================================================================*
01278  *                              f_geometry                                   *
01279  *===========================================================================*/
01280 PRIVATE void f_geometry(entry)
01281 struct partition *entry;
01282 {
01283   entry->cylinders = f_dp->cyls;
01284   entry->heads = NR_HEADS;
01285   entry->sectors = f_sectors;
01286 }
01287 

Generated on Fri Apr 14 22:57:16 2006 for minix by  doxygen 1.4.6