📄 ataflop.c
字号:
/* * drivers/block/ataflop.c * * Copyright (C) 1993 Greg Harp * Atari Support by Bjoern Brauel, Roman Hodek * * Big cleanup Sep 11..14 1994 Roman Hodek: * - Driver now works interrupt driven * - Support for two drives; should work, but I cannot test that :-( * - Reading is done in whole tracks and buffered to speed up things * - Disk change detection and drive deselecting after motor-off * similar to TOS * - Autodetection of disk format (DD/HD); untested yet, because I * don't have an HD drive :-( * * Fixes Nov 13 1994 Martin Schaller: * - Autodetection works now * - Support for 5 1/4'' disks * - Removed drive type (unknown on atari) * - Do seeks with 8 Mhz * * Changes by Andreas Schwab: * - After errors in multiple read mode try again reading single sectors * (Feb 1995): * - Clean up error handling * - Set blk_size for proper size checking * - Initialize track register when testing presence of floppy * - Implement some ioctl's * * Changes by Torsten Lang: * - When probing the floppies we should add the FDCCMDADD_H flag since * the FDC will otherwise wait forever when no disk is inserted... * * ++ Freddi Aschwanden (fa) 20.9.95 fixes for medusa: * - MFPDELAY() after each FDC access -> atari * - more/other disk formats * - DMA to the block buffer directly if we have a 32bit DMA * - for medusa, the step rate is always 3ms * - on medusa, use only cache_push() * Roman: * - Make disk format numbering independent from minors * - Let user set max. supported drive type (speeds up format * detection, saves buffer space) * * Roman 10/15/95: * - implement some more ioctls * - disk formatting * * Andreas 95/12/12: * - increase gap size at start of track for HD/ED disks * * Michael (MSch) 11/07/96: * - implemented FDSETPRM and FDDEFPRM ioctl * * Andreas (97/03/19): * - implemented missing BLK* ioctls * * Things left to do: * - Formatting * - Maybe a better strategy for disk change detection (does anyone * know one?) */#include <linux/module.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/fs.h>#include <linux/fcntl.h>#include <linux/kernel.h>#include <linux/timer.h>#include <linux/fd.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/delay.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/init.h>#include <asm/setup.h>#include <asm/system.h>#include <asm/bitops.h>#include <asm/irq.h>#include <asm/pgtable.h>#include <asm/uaccess.h>#include <asm/atafd.h>#include <asm/atafdreg.h>#include <asm/atarihw.h>#include <asm/atariints.h>#include <asm/atari_stdma.h>#include <asm/atari_stram.h>#define MAJOR_NR FLOPPY_MAJOR#include <linux/blk.h>#include <linux/blkpg.h>#define FD_MAX_UNITS 2#undef DEBUG/* Disk types: DD, HD, ED */static struct atari_disk_type { const char *name; unsigned spt; /* sectors per track */ unsigned blocks; /* total number of blocks */ unsigned fdc_speed; /* fdc_speed setting */ unsigned stretch; /* track doubling ? */} disk_type[] = { { "d360", 9, 720, 0, 0}, /* 0: 360kB diskette */ { "D360", 9, 720, 0, 1}, /* 1: 360kb in 720k or 1.2MB drive */ { "D720", 9,1440, 0, 0}, /* 2: 720kb in 720k or 1.2MB drive */ { "D820", 10,1640, 0, 0}, /* 3: DD disk with 82 tracks/10 sectors *//* formats above are probed for type DD */#define MAX_TYPE_DD 3 { "h1200",15,2400, 3, 0}, /* 4: 1.2MB diskette */ { "H1440",18,2880, 3, 0}, /* 5: 1.4 MB diskette (HD) */ { "H1640",20,3280, 3, 0}, /* 6: 1.64MB diskette (fat HD) 82 tr 20 sec *//* formats above are probed for types DD and HD */#define MAX_TYPE_HD 6 { "E2880",36,5760, 3, 0}, /* 7: 2.8 MB diskette (ED) */ { "E3280",40,6560, 3, 0}, /* 8: 3.2 MB diskette (fat ED) 82 tr 40 sec *//* formats above are probed for types DD, HD and ED */#define MAX_TYPE_ED 8/* types below are never autoprobed */ { "H1680",21,3360, 3, 0}, /* 9: 1.68MB diskette (fat HD) 80 tr 21 sec */ { "h410",10,820, 0, 1}, /* 10: 410k diskette 41 tr 10 sec, stretch */ { "h1476",18,2952, 3, 0}, /* 11: 1.48MB diskette 82 tr 18 sec */ { "H1722",21,3444, 3, 0}, /* 12: 1.72MB diskette 82 tr 21 sec */ { "h420",10,840, 0, 1}, /* 13: 420k diskette 42 tr 10 sec, stretch */ { "H830",10,1660, 0, 0}, /* 14: 820k diskette 83 tr 10 sec */ { "h1494",18,2952, 3, 0}, /* 15: 1.49MB diskette 83 tr 18 sec */ { "H1743",21,3486, 3, 0}, /* 16: 1.74MB diskette 83 tr 21 sec */ { "h880",11,1760, 0, 0}, /* 17: 880k diskette 80 tr 11 sec */ { "D1040",13,2080, 0, 0}, /* 18: 1.04MB diskette 80 tr 13 sec */ { "D1120",14,2240, 0, 0}, /* 19: 1.12MB diskette 80 tr 14 sec */ { "h1600",20,3200, 3, 0}, /* 20: 1.60MB diskette 80 tr 20 sec */ { "H1760",22,3520, 3, 0}, /* 21: 1.76MB diskette 80 tr 22 sec */ { "H1920",24,3840, 3, 0}, /* 22: 1.92MB diskette 80 tr 24 sec */ { "E3200",40,6400, 3, 0}, /* 23: 3.2MB diskette 80 tr 40 sec */ { "E3520",44,7040, 3, 0}, /* 24: 3.52MB diskette 80 tr 44 sec */ { "E3840",48,7680, 3, 0}, /* 25: 3.84MB diskette 80 tr 48 sec */ { "H1840",23,3680, 3, 0}, /* 26: 1.84MB diskette 80 tr 23 sec */ { "D800",10,1600, 0, 0}, /* 27: 800k diskette 80 tr 10 sec */};static int StartDiskType[] = { MAX_TYPE_DD, MAX_TYPE_HD, MAX_TYPE_ED};#define TYPE_DD 0#define TYPE_HD 1#define TYPE_ED 2static int DriveType = TYPE_HD;/* Array for translating minors into disk formats */static struct { int index; unsigned drive_types;} minor2disktype[] = { { 0, TYPE_DD }, /* 1: d360 */ { 4, TYPE_HD }, /* 2: h1200 */ { 1, TYPE_DD }, /* 3: D360 */ { 2, TYPE_DD }, /* 4: D720 */ { 1, TYPE_DD }, /* 5: h360 = D360 */ { 2, TYPE_DD }, /* 6: h720 = D720 */ { 5, TYPE_HD }, /* 7: H1440 */ { 7, TYPE_ED }, /* 8: E2880 *//* some PC formats :-) */ { 8, TYPE_ED }, /* 9: E3280 <- was "CompaQ" == E2880 for PC */ { 5, TYPE_HD }, /* 10: h1440 = H1440 */ { 9, TYPE_HD }, /* 11: H1680 */ { 10, TYPE_DD }, /* 12: h410 */ { 3, TYPE_DD }, /* 13: H820 <- == D820, 82x10 */ { 11, TYPE_HD }, /* 14: h1476 */ { 12, TYPE_HD }, /* 15: H1722 */ { 13, TYPE_DD }, /* 16: h420 */ { 14, TYPE_DD }, /* 17: H830 */ { 15, TYPE_HD }, /* 18: h1494 */ { 16, TYPE_HD }, /* 19: H1743 */ { 17, TYPE_DD }, /* 20: h880 */ { 18, TYPE_DD }, /* 21: D1040 */ { 19, TYPE_DD }, /* 22: D1120 */ { 20, TYPE_HD }, /* 23: h1600 */ { 21, TYPE_HD }, /* 24: H1760 */ { 22, TYPE_HD }, /* 25: H1920 */ { 23, TYPE_ED }, /* 26: E3200 */ { 24, TYPE_ED }, /* 27: E3520 */ { 25, TYPE_ED }, /* 28: E3840 */ { 26, TYPE_HD }, /* 29: H1840 */ { 27, TYPE_DD }, /* 30: D800 */ { 6, TYPE_HD }, /* 31: H1640 <- was H1600 == h1600 for PC */};#define NUM_DISK_MINORS (sizeof(minor2disktype)/sizeof(*minor2disktype))/* * Maximum disk size (in kilobytes). This default is used whenever the * current disk size is unknown. */#define MAX_DISK_SIZE 3280/* * MSch: User-provided type information. 'drive' points to * the respective entry of this array. Set by FDSETPRM ioctls. */static struct atari_disk_type user_params[FD_MAX_UNITS];/* * User-provided permanent type information. 'drive' points to * the respective entry of this array. Set by FDDEFPRM ioctls, * restored upon disk change by floppy_revalidate() if valid (as seen by * default_params[].blocks > 0 - a bit in unit[].flags might be used for this?) */static struct atari_disk_type default_params[FD_MAX_UNITS];static int floppy_sizes[256];static int floppy_blocksizes[256];/* current info on each unit */static struct atari_floppy_struct { int connected; /* !=0 : drive is connected */ int autoprobe; /* !=0 : do autoprobe */ struct atari_disk_type *disktype; /* current type of disk */ int track; /* current head position or -1 if unknown */ unsigned int steprate; /* steprate setting */ unsigned int wpstat; /* current state of WP signal (for disk change detection) */ int flags; /* flags */} unit[FD_MAX_UNITS];#define UD unit[drive]#define UDT unit[drive].disktype#define SUD unit[SelectedDrive]#define SUDT unit[SelectedDrive].disktype#define FDC_READ(reg) ({ \ /* unsigned long __flags; */ \ unsigned short __val; \ /* save_flags(__flags); cli(); */ \ dma_wd.dma_mode_status = 0x80 | (reg); \ udelay(25); \ __val = dma_wd.fdc_acces_seccount; \ MFPDELAY(); \ /* restore_flags(__flags); */ \ __val & 0xff; \})#define FDC_WRITE(reg,val) \ do { \ /* unsigned long __flags; */ \ /* save_flags(__flags); cli(); */ \ dma_wd.dma_mode_status = 0x80 | (reg); \ udelay(25); \ dma_wd.fdc_acces_seccount = (val); \ MFPDELAY(); \ /* restore_flags(__flags); */ \ } while(0)/* Buffering variables: * First, there is a DMA buffer in ST-RAM that is used for floppy DMA * operations. Second, a track buffer is used to cache a whole track * of the disk to save read operations. These are two separate buffers * because that allows write operations without clearing the track buffer. */static int MaxSectors[] = { 11, 22, 44};static int BufferSize[] = { 15*512, 30*512, 60*512};#define BUFFER_SIZE (BufferSize[DriveType])unsigned char *DMABuffer; /* buffer for writes */static unsigned long PhysDMABuffer; /* physical address */static int UseTrackbuffer = -1; /* Do track buffering? */MODULE_PARM(UseTrackbuffer, "i");unsigned char *TrackBuffer; /* buffer for reads */static unsigned long PhysTrackBuffer; /* physical address */static int BufferDrive, BufferSide, BufferTrack;static int read_track; /* non-zero if we are reading whole tracks */#define SECTOR_BUFFER(sec) (TrackBuffer + ((sec)-1)*512)#define IS_BUFFERED(drive,side,track) \ (BufferDrive == (drive) && BufferSide == (side) && BufferTrack == (track))/* * These are global variables, as that's the easiest way to give * information to interrupts. They are the data used for the current * request. */static int SelectedDrive = 0;static int ReqCmd, ReqBlock;static int ReqSide, ReqTrack, ReqSector, ReqCnt;static int HeadSettleFlag = 0;static unsigned char *ReqData, *ReqBuffer;static int MotorOn = 0, MotorOffTrys;static int IsFormatting = 0, FormatError;static int UserSteprate[FD_MAX_UNITS] = { -1, -1 };MODULE_PARM(UserSteprate, "1-" __MODULE_STRING(FD_MAX_UNITS) "i");/* Synchronization of FDC access. */static volatile int fdc_busy = 0;static DECLARE_WAIT_QUEUE_HEAD(fdc_wait);static DECLARE_WAIT_QUEUE_HEAD(format_wait);static unsigned long changed_floppies = 0xff, fake_change = 0;#define CHECK_CHANGE_DELAY HZ/2#define FD_MOTOR_OFF_DELAY (3*HZ)#define FD_MOTOR_OFF_MAXTRY (10*20)#define FLOPPY_TIMEOUT (6*HZ)#define RECALIBRATE_ERRORS 4 /* After this many errors the drive * will be recalibrated. */#define MAX_ERRORS 8 /* After this many errors the driver * will give up. *//* * The driver is trying to determine the correct media format * while Probing is set. fd_rwsec_done() clears it after a * successful access. */static int Probing = 0;/* This flag is set when a dummy seek is necessary to make the WP * status bit accessible. */static int NeedSeek = 0;#ifdef DEBUG#define DPRINT(a) printk a#else#define DPRINT(a)#endif/***************************** Prototypes *****************************/static void fd_select_side( int side );static void fd_select_drive( int drive );static void fd_deselect( void );static void fd_motor_off_timer( unsigned long dummy );static void check_change( void );static __inline__ void set_head_settle_flag( void );static __inline__ int get_head_settle_flag( void );static void floppy_irq (int irq, void *dummy, struct pt_regs *fp);static void fd_error( void );static int do_format(kdev_t drive, struct atari_format_descr *desc);static void do_fd_action( int drive );static void fd_calibrate( void );static void fd_calibrate_done( int status );static void fd_seek( void );static void fd_seek_done( int status );static void fd_rwsec( void );static void fd_readtrack_check( unsigned long dummy );static void fd_rwsec_done( int status );static void fd_rwsec_done1(int status);static void fd_writetrack( void );static void fd_writetrack_done( int status );static void fd_times_out( unsigned long dummy );static void finish_fdc( void );static void finish_fdc_done( int dummy );static void floppy_off( unsigned int nr);static __inline__ void copy_buffer( void *from, void *to);static void setup_req_params( int drive );static void redo_fd_request( void);static int invalidate_drive(kdev_t rdev);static int fd_ioctl( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param);static void fd_probe( int drive );static int fd_test_drive_present( int drive );static void config_types( void );static int floppy_open( struct inode *inode, struct file *filp );static int floppy_release( struct inode * inode, struct file * filp );/************************* End of Prototypes **************************/static struct timer_list motor_off_timer = { function: fd_motor_off_timer };static struct timer_list readtrack_timer = { function: fd_readtrack_check };static struct timer_list timeout_timer = { function: fd_times_out };static struct timer_list fd_timer = { function: check_change }; static inline voidstart_motor_off_timer(void){ mod_timer(&motor_off_timer, jiffies + FD_MOTOR_OFF_DELAY); MotorOffTrys = 0;}static inline voidstart_check_change_timer( void ){ mod_timer(&fd_timer, jiffies + CHECK_CHANGE_DELAY);}static inline voidstart_timeout(void){ mod_timer(&timeout_timer, jiffies + FLOPPY_TIMEOUT);}static inline voidstop_timeout(void){ del_timer(&timeout_timer);}/* Select the side to use. */static void fd_select_side( int side ){ unsigned long flags; save_flags(flags); cli(); /* protect against various other ints mucking around with the PSG */ sound_ym.rd_data_reg_sel = 14; /* Select PSG Port A */ sound_ym.wd_data = (side == 0) ? sound_ym.rd_data_reg_sel | 0x01 : sound_ym.rd_data_reg_sel & 0xfe; restore_flags(flags);}/* Select a drive, update the FDC's track register and set the correct * clock speed for this disk's type. */static void fd_select_drive( int drive ){ unsigned long flags; unsigned char tmp; if (drive == SelectedDrive) return; save_flags(flags); cli(); /* protect against various other ints mucking around with the PSG */ sound_ym.rd_data_reg_sel = 14; /* Select PSG Port A */ tmp = sound_ym.rd_data_reg_sel; sound_ym.wd_data = (tmp | DSKDRVNONE) & ~(drive == 0 ? DSKDRV0 : DSKDRV1); atari_dont_touch_floppy_select = 1; restore_flags(flags); /* restore track register to saved value */ FDC_WRITE( FDCREG_TRACK, UD.track ); udelay(25); /* select 8/16 MHz */ if (UDT) if (ATARIHW_PRESENT(FDCSPEED)) dma_wd.fdc_speed = UDT->fdc_speed; SelectedDrive = drive;}/* Deselect both drives. */static void fd_deselect( void ){ unsigned long flags; save_flags(flags); cli(); /* protect against various other ints mucking around with the PSG */ atari_dont_touch_floppy_select = 0; sound_ym.rd_data_reg_sel=14; /* Select PSG Port A */ sound_ym.wd_data = (sound_ym.rd_data_reg_sel | (MACH_IS_FALCON ? 3 : 7)); /* no drives selected */ /* On Falcon, the drive B select line is used on the printer port, so * leave it alone... */ SelectedDrive = -1; restore_flags(flags);}/* This timer function deselects the drives when the FDC switched the * motor off. The deselection cannot happen earlier because the FDC * counts the index signals, which arrive only if one drive is selected. */static void fd_motor_off_timer( unsigned long dummy ){ unsigned char status; if (SelectedDrive < 0) /* no drive selected, needn't deselect anyone */ return; if (stdma_islocked()) goto retry; status = FDC_READ( FDCREG_STATUS ); if (!(status & 0x80)) { /* motor already turned off by FDC -> deselect drives */ MotorOn = 0; fd_deselect(); return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -