📄 ataflop.c
字号:
local_irq_restore(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 */ local_irq_restore(flags); DPRINT(("fd_readtrack_check(): not yet finished\n")); mod_timer(&readtrack_timer, jiffies + HZ/5/10); }}static void fd_rwsec_done( int status ){ DPRINT(("fd_rwsec_done()\n")); if (read_track) { del_timer(&readtrack_timer); if (!MultReadInProgress) return; MultReadInProgress = 0; } fd_rwsec_done1(status);}static void fd_rwsec_done1(int status){ unsigned int track; stop_timeout(); /* Correct the track if stretch != 0 */ if (SUDT->stretch) { track = FDC_READ( FDCREG_TRACK); MFPDELAY(); FDC_WRITE( FDCREG_TRACK, track << SUDT->stretch); } if (!UseTrackbuffer) { dma_wd.dma_mode_status = 0x90; MFPDELAY(); if (!(dma_wd.dma_mode_status & 0x01)) { printk(KERN_ERR "fd%d: DMA error\n", SelectedDrive ); goto err_end; } } MFPDELAY(); if (ReqCmd == WRITE && (status & FDCSTAT_WPROT)) { printk(KERN_NOTICE "fd%d: is write protected\n", SelectedDrive ); goto err_end; } if ((status & FDCSTAT_RECNF) && /* RECNF is no error after a multiple read when the FDC searched for a non-existent sector! */ !(read_track && FDC_READ(FDCREG_SECTOR) > SUDT->spt)) { if (Probing) { if (SUDT > disk_type) { if (SUDT[-1].blocks > ReqBlock) { /* try another disk type */ SUDT--; set_capacity(unit[SelectedDrive].disk, SUDT->blocks); } else Probing = 0; } else { if (SUD.flags & FTD_MSG) printk(KERN_INFO "fd%d: Auto-detected floppy type %s\n", SelectedDrive, SUDT->name ); Probing=0; } } else { /* record not found, but not probing. Maybe stretch wrong ? Restart probing */ if (SUD.autoprobe) { SUDT = disk_type + StartDiskType[DriveType]; set_capacity(unit[SelectedDrive].disk, SUDT->blocks); Probing = 1; } } if (Probing) { if (ATARIHW_PRESENT(FDCSPEED)) { dma_wd.fdc_speed = SUDT->fdc_speed; MFPDELAY(); } setup_req_params( SelectedDrive ); BufferDrive = -1; do_fd_action( SelectedDrive ); return; } printk(KERN_ERR "fd%d: sector %d not found (side %d, track %d)\n", SelectedDrive, FDC_READ (FDCREG_SECTOR), ReqSide, ReqTrack ); goto err_end; } if (status & FDCSTAT_CRC) { printk(KERN_ERR "fd%d: CRC error (side %d, track %d, sector %d)\n", SelectedDrive, ReqSide, ReqTrack, FDC_READ (FDCREG_SECTOR) ); goto err_end; } if (status & FDCSTAT_LOST) { printk(KERN_ERR "fd%d: lost data (side %d, track %d, sector %d)\n", SelectedDrive, ReqSide, ReqTrack, FDC_READ (FDCREG_SECTOR) ); goto err_end; } Probing = 0; if (ReqCmd == READ) { if (!read_track) { void *addr; addr = ATARIHW_PRESENT( EXTD_DMA ) ? ReqData : DMABuffer; dma_cache_maintenance( virt_to_phys(addr), 512, 0 ); if (!ATARIHW_PRESENT( EXTD_DMA )) copy_buffer (addr, ReqData); } else { dma_cache_maintenance( PhysTrackBuffer, MaxSectors[DriveType] * 512, 0 ); BufferDrive = SelectedDrive; BufferSide = ReqSide; BufferTrack = ReqTrack; copy_buffer (SECTOR_BUFFER (ReqSector), ReqData); } } if (++ReqCnt < CURRENT->current_nr_sectors) { /* read next sector */ setup_req_params( SelectedDrive ); do_fd_action( SelectedDrive ); } else { /* all sectors finished */ CURRENT->nr_sectors -= CURRENT->current_nr_sectors; CURRENT->sector += CURRENT->current_nr_sectors; end_request(CURRENT, 1); redo_fd_request(); } return; err_end: BufferDrive = -1; fd_error();}static void fd_writetrack( void ){ unsigned long paddr, flags; unsigned int track; DPRINT(("fd_writetrack() Tr=%d Si=%d\n", ReqTrack, ReqSide )); paddr = PhysTrackBuffer; dma_cache_maintenance( paddr, BUFFER_SIZE, 1 ); fd_select_side( ReqSide ); /* Cheat for track if stretch != 0 */ if (SUDT->stretch) { track = FDC_READ( FDCREG_TRACK); MFPDELAY(); FDC_WRITE(FDCREG_TRACK,track >> SUDT->stretch); } udelay(40); /* Setup DMA */ local_irq_save(flags); 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(); local_irq_restore(flags); /* Clear FIFO and switch DMA to correct mode */ dma_wd.dma_mode_status = 0x190; MFPDELAY(); dma_wd.dma_mode_status = 0x90; MFPDELAY(); dma_wd.dma_mode_status = 0x190; MFPDELAY(); /* How many sectors for DMA */ dma_wd.fdc_acces_seccount = BUFFER_SIZE/512; udelay(40); /* Start operation */ dma_wd.dma_mode_status = FDCSELREG_STP | 0x100; udelay(40); SET_IRQ_HANDLER( fd_writetrack_done ); dma_wd.fdc_acces_seccount = FDCCMD_WRTRA | get_head_settle_flag(); MotorOn = 1; start_timeout(); /* wait for interrupt */}static void fd_writetrack_done( int status ){ DPRINT(("fd_writetrack_done()\n")); stop_timeout(); if (status & FDCSTAT_WPROT) { printk(KERN_NOTICE "fd%d: is write protected\n", SelectedDrive ); goto err_end; } if (status & FDCSTAT_LOST) { printk(KERN_ERR "fd%d: lost data (side %d, track %d)\n", SelectedDrive, ReqSide, ReqTrack ); goto err_end; } wake_up( &format_wait ); return; err_end: fd_error();}static void fd_times_out( unsigned long dummy ){ atari_disable_irq( IRQ_MFP_FDC ); if (!FloppyIRQHandler) goto end; /* int occurred after timer was fired, but * before we came here... */ SET_IRQ_HANDLER( NULL ); /* If the timeout occurred while the readtrack_check timer was * active, we need to cancel it, else bad things will happen */ if (UseTrackbuffer) del_timer( &readtrack_timer ); FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI ); udelay( 25 ); printk(KERN_ERR "floppy timeout\n" ); fd_error(); end: atari_enable_irq( IRQ_MFP_FDC );}/* The (noop) seek operation here is needed to make the WP bit in the * FDC status register accessible for check_change. If the last disk * operation would have been a RDSEC, this bit would always read as 0 * no matter what :-( To save time, the seek goes to the track we're * already on. */static void finish_fdc( void ){ if (!NeedSeek) { finish_fdc_done( 0 ); } else { DPRINT(("finish_fdc: dummy seek started\n")); FDC_WRITE (FDCREG_DATA, SUD.track); SET_IRQ_HANDLER( finish_fdc_done ); FDC_WRITE (FDCREG_CMD, FDCCMD_SEEK); MotorOn = 1; start_timeout(); /* we must wait for the IRQ here, because the ST-DMA is released immediately afterwards and the interrupt may be delivered to the wrong driver. */ }}static void finish_fdc_done( int dummy ){ unsigned long flags; DPRINT(("finish_fdc_done entered\n")); stop_timeout(); NeedSeek = 0; if (timer_pending(&fd_timer) && time_before(fd_timer.expires, jiffies + 5)) /* If the check for a disk change is done too early after this * last seek command, the WP bit still reads wrong :-(( */ mod_timer(&fd_timer, jiffies + 5); else start_check_change_timer(); start_motor_off_timer(); local_irq_save(flags); stdma_release(); fdc_busy = 0; wake_up( &fdc_wait ); local_irq_restore(flags); DPRINT(("finish_fdc() finished\n"));}/* The detection of disk changes is a dark chapter in Atari history :-( * Because the "Drive ready" signal isn't present in the Atari * hardware, one has to rely on the "Write Protect". This works fine, * as long as no write protected disks are used. TOS solves this * problem by introducing tri-state logic ("maybe changed") and * looking at the serial number in block 0. This isn't possible for * Linux, since the floppy driver can't make assumptions about the * filesystem used on the disk and thus the contents of block 0. I've * chosen the method to always say "The disk was changed" if it is * unsure whether it was. This implies that every open or mount * invalidates the disk buffers if you work with write protected * disks. But at least this is better than working with incorrect data * due to unrecognised disk changes. */static int check_floppy_change(struct gendisk *disk){ struct atari_floppy_struct *p = disk->private_data; unsigned int drive = p - unit; if (test_bit (drive, &fake_change)) { /* simulated change (e.g. after formatting) */ return 1; } if (test_bit (drive, &changed_floppies)) { /* surely changed (the WP signal changed at least once) */ return 1; } if (UD.wpstat) { /* WP is on -> could be changed: to be sure, buffers should be * invalidated... */ return 1; } return 0;}static int floppy_revalidate(struct gendisk *disk){ struct atari_floppy_struct *p = disk->private_data; unsigned int drive = p - unit; if (test_bit(drive, &changed_floppies) || test_bit(drive, &fake_change) || p->disktype == 0) { if (UD.flags & FTD_MSG) printk(KERN_ERR "floppy: clear format %p!\n", UDT); BufferDrive = -1; clear_bit(drive, &fake_change); clear_bit(drive, &changed_floppies); /* MSch: clearing geometry makes sense only for autoprobe formats, for 'permanent user-defined' parameter: restore default_params[] here if flagged valid! */ if (default_params[drive].blocks == 0) UDT = 0; else UDT = &default_params[drive]; } return 0;}/* This sets up the global variables describing the current request. */static void setup_req_params( int drive ){ int block = ReqBlock + ReqCnt; ReqTrack = block / UDT->spt; ReqSector = block - ReqTrack * UDT->spt + 1; ReqSide = ReqTrack & 1; ReqTrack >>= 1; ReqData = ReqBuffer + 512 * ReqCnt; if (UseTrackbuffer) read_track = (ReqCmd == READ && CURRENT->errors == 0); else read_track = 0; DPRINT(("Request params: Si=%d Tr=%d Se=%d Data=%08lx\n",ReqSide, ReqTrack, ReqSector, (unsigned long)ReqData ));}static void redo_fd_request(void){ int drive, type; struct atari_floppy_struct *floppy; DPRINT(("redo_fd_request: CURRENT=%p dev=%s CURRENT->sector=%ld\n", CURRENT, CURRENT ? CURRENT->rq_disk->disk_name : "", CURRENT ? CURRENT->sector : 0 )); IsFormatting = 0;repeat: if (!CURRENT) goto the_end; floppy = CURRENT->rq_disk->private_data; drive = floppy - unit; type = floppy->type; if (!UD.connected) { /* drive not connected */ printk(KERN_ERR "Unknown Device: fd%d\n", drive ); end_request(CURRENT, 0); goto repeat; } if (type == 0) { if (!UDT) { Probing = 1; UDT = disk_type + StartDiskType[DriveType]; set_capacity(floppy->disk, UDT->blocks); UD.autoprobe = 1; } } else { /* user supplied disk type */ if (--type >= NUM_DISK_MINORS) { printk(KERN_WARNING "fd%d: invalid disk format", drive ); end_request(CURRENT, 0); goto repeat; } if (minor2disktype[type].drive_types > DriveType) { printk(KERN_WARNING "fd%d: unsupported disk format", drive ); end_request(CURRENT, 0); goto repeat; } type = minor2disktype[type].index; UDT = &disk_type[type]; set_capacity(floppy->disk, UDT->blocks); UD.autoprobe = 0; } if (CURRENT->sector + 1 > UDT->blocks) { end_request(CURRENT, 0); goto repeat; } /* stop deselect timer */ del_timer( &motor_off_timer ); ReqCnt = 0; ReqCmd = rq_data_dir(CURRENT); ReqBlock = CURRENT->sector; ReqBuffer = CURRENT->buffer; setup_req_params( drive ); do_fd_action( drive ); return; the_end: finish_fdc();}void do_fd_request(request_queue_t * q){ unsigned long flags; DPRINT(("do_fd_request for pid %d\n",current->pid)); while( fdc_busy ) sleep_on( &fdc_wait ); fdc_busy = 1; stdma_lock(floppy_irq, NULL); atari_disable_irq( IRQ_MFP_FDC ); local_save_flags(flags); /* The request function is called with ints local_irq_disable(); * disabled... so must save the IPL for later */ redo_fd_request(); local_irq_restore(flags); atari_enable_irq( IRQ_MFP_FDC );}static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param){ struct gendisk *disk = inode->i_bdev->bd_disk; struct atari_floppy_struct *floppy = disk->private_data; int drive = floppy - unit; int type = floppy->type; struct atari_format_descr fmt_desc; struct atari_disk_type *dtp; struct floppy_struct getprm; int settype; struct floppy_struct setprm; switch (cmd) { case FDGETPRM: if (type) { if (--type >= NUM_DISK_MINORS) return -ENODEV;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -