📄 ataflop.c
字号:
return; } /* not yet off, try again */ retry: /* Test again later; if tested too often, it seems there is no disk * in the drive and the FDC will leave the motor on forever (or, * at least until a disk is inserted). So we'll test only twice * per second from then on... */ mod_timer(&motor_off_timer, jiffies + (MotorOffTrys++ < FD_MOTOR_OFF_MAXTRY ? HZ/20 : HZ/2));}/* This function is repeatedly called to detect disk changes (as good * as possible) and keep track of the current state of the write protection. */static void check_change( void ){ static int drive = 0; unsigned long flags; unsigned char old_porta; int stat; if (++drive > 1 || !UD.connected) drive = 0; save_flags(flags); cli(); /* protect against various other ints mucking around with the PSG */ if (!stdma_islocked()) { sound_ym.rd_data_reg_sel = 14; old_porta = sound_ym.rd_data_reg_sel; sound_ym.wd_data = (old_porta | DSKDRVNONE) & ~(drive == 0 ? DSKDRV0 : DSKDRV1); stat = !!(FDC_READ( FDCREG_STATUS ) & FDCSTAT_WPROT); sound_ym.wd_data = old_porta; if (stat != UD.wpstat) { DPRINT(( "wpstat[%d] = %d\n", drive, stat )); UD.wpstat = stat; set_bit (drive, &changed_floppies); } } restore_flags(flags); start_check_change_timer();} /* Handling of the Head Settling Flag: This flag should be set after each * seek operation, because we don't use seeks with verify. */static __inline__ void set_head_settle_flag( void ){ HeadSettleFlag = FDCCMDADD_E;}static __inline__ int get_head_settle_flag( void ){ int tmp = HeadSettleFlag; HeadSettleFlag = 0; return( tmp );} /* General Interrupt Handling */static void (*FloppyIRQHandler)( int status ) = NULL;static void floppy_irq (int irq, void *dummy, struct pt_regs *fp){ unsigned char status; void (*handler)( int ); handler = xchg(&FloppyIRQHandler, NULL); if (handler) { nop(); status = FDC_READ( FDCREG_STATUS ); DPRINT(("FDC irq, status = %02x handler = %08lx\n",status,(unsigned long)handler)); handler( status ); } else { DPRINT(("FDC irq, no handler\n")); }}/* Error handling: If some error happened, retry some times, then * recalibrate, then try again, and fail after MAX_ERRORS. */static void fd_error( void ){ if (IsFormatting) { IsFormatting = 0; FormatError = 1; wake_up( &format_wait ); return; } if (QUEUE_EMPTY) return; CURRENT->errors++; if (CURRENT->errors >= MAX_ERRORS) { printk(KERN_ERR "fd%d: too many errors.\n", SelectedDrive ); end_request( 0 ); } else if (CURRENT->errors == RECALIBRATE_ERRORS) { printk(KERN_WARNING "fd%d: recalibrating\n", SelectedDrive ); if (SelectedDrive != -1) SUD.track = -1; } redo_fd_request();}#define SET_IRQ_HANDLER(proc) do { FloppyIRQHandler = (proc); } while(0)/* ---------- Formatting ---------- */#define FILL(n,val) \ do { \ memset( p, val, n ); \ p += n; \ } while(0)static int do_format(kdev_t device, struct atari_format_descr *desc){ unsigned char *p; int sect, nsect; unsigned long flags; int type, drive = MINOR(device) & 3; DPRINT(("do_format( dr=%d tr=%d he=%d offs=%d )\n", drive, desc->track, desc->head, desc->sect_offset )); save_flags(flags); cli(); while( fdc_busy ) sleep_on( &fdc_wait ); fdc_busy = 1; stdma_lock(floppy_irq, NULL); atari_turnon_irq( IRQ_MFP_FDC ); /* should be already, just to be sure */ restore_flags(flags); type = MINOR(device) >> 2; if (type) { if (--type >= NUM_DISK_MINORS || minor2disktype[type].drive_types > DriveType) { redo_fd_request(); return -EINVAL; } type = minor2disktype[type].index; UDT = &disk_type[type]; } if (!UDT || desc->track >= UDT->blocks/UDT->spt/2 || desc->head >= 2) { redo_fd_request(); return -EINVAL; } nsect = UDT->spt; p = TrackBuffer; /* The track buffer is used for the raw track data, so its contents become invalid! */ BufferDrive = -1; /* stop deselect timer */ del_timer( &motor_off_timer ); FILL( 60 * (nsect / 9), 0x4e ); for( sect = 0; sect < nsect; ++sect ) { FILL( 12, 0 ); FILL( 3, 0xf5 ); *p++ = 0xfe; *p++ = desc->track; *p++ = desc->head; *p++ = (nsect + sect - desc->sect_offset) % nsect + 1; *p++ = 2; *p++ = 0xf7; FILL( 22, 0x4e ); FILL( 12, 0 ); FILL( 3, 0xf5 ); *p++ = 0xfb; FILL( 512, 0xe5 ); *p++ = 0xf7; FILL( 40, 0x4e ); } FILL( TrackBuffer+BUFFER_SIZE-p, 0x4e ); IsFormatting = 1; FormatError = 0; ReqTrack = desc->track; ReqSide = desc->head; do_fd_action( drive ); sleep_on( &format_wait ); redo_fd_request(); return( FormatError ? -EIO : 0 ); }/* do_fd_action() is the general procedure for a fd request: All * required parameter settings (drive select, side select, track * position) are checked and set if needed. For each of these * parameters and the actual reading or writing exist two functions: * one that starts the setting (or skips it if possible) and one * callback for the "done" interrupt. Each done func calls the next * set function to propagate the request down to fd_rwsec_done(). */static void do_fd_action( int drive ){ DPRINT(("do_fd_action\n")); if (UseTrackbuffer && !IsFormatting) { repeat: if (IS_BUFFERED( drive, ReqSide, ReqTrack )) { if (ReqCmd == READ) { copy_buffer( SECTOR_BUFFER(ReqSector), ReqData ); if (++ReqCnt < CURRENT->current_nr_sectors) { /* read next sector */ setup_req_params( drive ); goto repeat; } else { /* all sectors finished */ CURRENT->nr_sectors -= CURRENT->current_nr_sectors; CURRENT->sector += CURRENT->current_nr_sectors; end_request( 1 ); redo_fd_request(); return; } } else { /* cmd == WRITE, pay attention to track buffer * consistency! */ copy_buffer( ReqData, SECTOR_BUFFER(ReqSector) ); } } } if (SelectedDrive != drive) fd_select_drive( drive ); if (UD.track == -1) fd_calibrate(); else if (UD.track != ReqTrack << UDT->stretch) fd_seek(); else if (IsFormatting) fd_writetrack(); else fd_rwsec();}/* Seek to track 0 if the current track is unknown */static void fd_calibrate( void ){ if (SUD.track >= 0) { fd_calibrate_done( 0 ); return; } if (ATARIHW_PRESENT(FDCSPEED)) dma_wd.fdc_speed = 0; /* always seek with 8 Mhz */; DPRINT(("fd_calibrate\n")); SET_IRQ_HANDLER( fd_calibrate_done ); /* we can't verify, since the speed may be incorrect */ FDC_WRITE( FDCREG_CMD, FDCCMD_RESTORE | SUD.steprate ); NeedSeek = 1; MotorOn = 1; start_timeout(); /* wait for IRQ */}static void fd_calibrate_done( int status ){ DPRINT(("fd_calibrate_done()\n")); stop_timeout(); /* set the correct speed now */ if (ATARIHW_PRESENT(FDCSPEED)) dma_wd.fdc_speed = SUDT->fdc_speed; if (status & FDCSTAT_RECNF) { printk(KERN_ERR "fd%d: restore failed\n", SelectedDrive ); fd_error(); } else { SUD.track = 0; fd_seek(); }} /* Seek the drive to the requested track. The drive must have been * calibrated at some point before this. */ static void fd_seek( void ){ if (SUD.track == ReqTrack << SUDT->stretch) { fd_seek_done( 0 ); return; } if (ATARIHW_PRESENT(FDCSPEED)) { dma_wd.fdc_speed = 0; /* always seek witch 8 Mhz */ MFPDELAY(); } DPRINT(("fd_seek() to track %d\n",ReqTrack)); FDC_WRITE( FDCREG_DATA, ReqTrack << SUDT->stretch); udelay(25); SET_IRQ_HANDLER( fd_seek_done ); FDC_WRITE( FDCREG_CMD, FDCCMD_SEEK | SUD.steprate ); MotorOn = 1; set_head_settle_flag(); start_timeout(); /* wait for IRQ */}static void fd_seek_done( int status ){ DPRINT(("fd_seek_done()\n")); stop_timeout(); /* set the correct speed */ if (ATARIHW_PRESENT(FDCSPEED)) dma_wd.fdc_speed = SUDT->fdc_speed; if (status & FDCSTAT_RECNF) { printk(KERN_ERR "fd%d: seek error (to track %d)\n", SelectedDrive, ReqTrack ); /* we don't know exactly which track we are on now! */ SUD.track = -1; fd_error(); } else { SUD.track = ReqTrack << SUDT->stretch; NeedSeek = 0; if (IsFormatting) fd_writetrack(); else fd_rwsec(); }}/* This does the actual reading/writing after positioning the head * over the correct track. */static int MultReadInProgress = 0;static void fd_rwsec( void ){ unsigned long paddr, flags; unsigned int rwflag, old_motoron; unsigned int track; DPRINT(("fd_rwsec(), Sec=%d, Access=%c\n",ReqSector, ReqCmd == WRITE ? 'w' : 'r' )); if (ReqCmd == WRITE) { if (ATARIHW_PRESENT(EXTD_DMA)) { paddr = virt_to_phys(ReqData); } else { copy_buffer( ReqData, DMABuffer ); paddr = PhysDMABuffer; } dma_cache_maintenance( paddr, 512, 1 ); rwflag = 0x100; } else { if (read_track) paddr = PhysTrackBuffer; else paddr = ATARIHW_PRESENT(EXTD_DMA) ? virt_to_phys(ReqData) : PhysDMABuffer; rwflag = 0; } fd_select_side( ReqSide ); /* Start sector of this operation */ FDC_WRITE( FDCREG_SECTOR, read_track ? 1 : ReqSector ); MFPDELAY(); /* Cheat for track if stretch != 0 */ if (SUDT->stretch) { track = FDC_READ( FDCREG_TRACK); MFPDELAY(); FDC_WRITE( FDCREG_TRACK, track >> SUDT->stretch); } udelay(25); /* Setup DMA */ save_flags(flags); cli(); dma_wd.dma_lo = (unsigned char)paddr; MFPDELAY(); paddr >>= 8; dma_wd.dma_md = (unsigned char)paddr; MFPDELAY(); paddr >>= 8; if (ATARIHW_PRESENT(EXTD_DMA)) st_dma_ext_dmahi = (unsigned short)paddr; else dma_wd.dma_hi = (unsigned char)paddr; MFPDELAY(); restore_flags(flags); /* Clear FIFO and switch DMA to correct mode */ dma_wd.dma_mode_status = 0x90 | rwflag; MFPDELAY(); dma_wd.dma_mode_status = 0x90 | (rwflag ^ 0x100); MFPDELAY(); dma_wd.dma_mode_status = 0x90 | rwflag; MFPDELAY(); /* How many sectors for DMA */ dma_wd.fdc_acces_seccount = read_track ? SUDT->spt : 1; udelay(25); /* Start operation */ dma_wd.dma_mode_status = FDCSELREG_STP | rwflag; udelay(25); SET_IRQ_HANDLER( fd_rwsec_done ); dma_wd.fdc_acces_seccount = (get_head_settle_flag() | (rwflag ? FDCCMD_WRSEC : (FDCCMD_RDSEC | (read_track ? FDCCMDADD_M : 0)))); old_motoron = MotorOn; MotorOn = 1; NeedSeek = 1; /* wait for interrupt */ if (read_track) { /* If reading a whole track, wait about one disk rotation and * then check if all sectors are read. The FDC will even * search for the first non-existent sector and need 1 sec to * recognise that it isn't present :-( */ MultReadInProgress = 1; mod_timer(&readtrack_timer, /* 1 rot. + 5 rot.s if motor was off */ jiffies + HZ/5 + (old_motoron ? 0 : HZ)); } start_timeout();} static void fd_readtrack_check( unsigned long dummy ){ unsigned long flags, addr, addr2; save_flags(flags); cli(); if (!MultReadInProgress) { /* This prevents a race condition that could arise if the * interrupt is triggered while the calling of this timer * callback function takes place. The IRQ function then has * already cleared 'MultReadInProgress' when flow of control * gets here. */ restore_flags(flags); return; } /* get the current DMA address */ /* ++ f.a. read twice to avoid being fooled by switcher */ addr = 0; do { addr2 = addr; addr = dma_wd.dma_lo & 0xff; MFPDELAY(); addr |= (dma_wd.dma_md & 0xff) << 8; MFPDELAY(); if (ATARIHW_PRESENT( EXTD_DMA )) addr |= (st_dma_ext_dmahi & 0xffff) << 16; else addr |= (dma_wd.dma_hi & 0xff) << 16; MFPDELAY(); } while(addr != addr2); if (addr >= PhysTrackBuffer + SUDT->spt*512) { /* already read enough data, force an FDC interrupt to stop * the read operation */ SET_IRQ_HANDLER( NULL ); MultReadInProgress = 0; restore_flags(flags); DPRINT(("fd_readtrack_check(): done\n")); FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI ); udelay(25); /* No error until now -- the FDC would have interrupted * otherwise! */ fd_rwsec_done1(0); } else { /* not yet finished, wait another tenth rotation */ restore_flags(flags); DPRINT(("fd_readtrack_check(): not yet finished\n"));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -