/* current info on each unit */ staticstruct 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 */ unsignedint steprate; /* steprate setting */ unsignedint wpstat; /* current state of WP signal (for
disk change detection) */ int flags; /* flags */ struct gendisk *disk[NUM_DISK_MINORS]; bool registered[NUM_DISK_MINORS]; int ref; int type; struct blk_mq_tag_set tag_set; int error_count;
} unit[FD_MAX_UNITS];
#define UD unit[drive] #define UDT unit[drive].disktype #define SUD unit[SelectedDrive] #define SUDT unit[SelectedDrive].disktype
#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. */
/* protect against various other ints mucking around with the PSG */
local_irq_save(flags);
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;
local_irq_restore(flags);
}
/* This timer function deselects the drives when the FDC switched the *motoroff.ThedeselectioncannothappenearlierbecausetheFDC *countstheindexsignals,whicharriveonlyifonedriveisselected.
*/
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;
} /* not yet off, try again */
retry: /* Test again later; if tested too often, it seems there is no disk *inthedriveandtheFDCwillleavethemotoronforever(or, *atleastuntiladiskisinserted).Sowe'lltestonlytwice *persecondfromthenon...
*/
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 *aspossible)andkeeptrackofthecurrentstateofthewriteprotection.
*/
local_irq_save(flags);
stdma_lock(floppy_irq, NULL);
atari_turnon_irq( IRQ_MFP_FDC ); /* should be already, just to be sure */
local_irq_restore(flags);
if (type) {
type = minor2disktype[type].index;
UDT = &atari_disk_type[type];
}
if (!UDT || desc->track >= UDT->blocks/UDT->spt/2 || desc->head >= 2) {
finish_fdc();
ret = -EINVAL; goto out;
}
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 */
timer_delete(&motor_off_timer);
/* do_fd_action() is the general procedure for a fd request: All *requiredparametersettings(driveselect,sideselect,track *position)arecheckedandsetifneeded.Foreachofthese *parametersandtheactualreadingorwritingexisttwofunctions: *onethatstartsthesetting(orskipsitifpossible)andone *callbackforthe"done"interrupt.Eachdonefunccallsthenext *setfunctiontopropagatetherequestdowntofd_rwsec_done().
*/
staticvoid do_fd_action( int drive )
{
DPRINT(("do_fd_action\n"));
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 );
if (read_track) { /* If reading a whole track, wait about one disk rotation and *thencheckifallsectorsareread.TheFDCwilleven *searchforthefirstnon-existentsectorandneed1secto *recognisethatitisn'tpresent:-(
*/
MultReadInProgress = 1;
mod_timer(&readtrack_timer, /* 1 rot. + 5 rot.s if motor was off */
jiffies + HZ/5 + (old_motoron ? 0 : HZ));
}
start_timeout();
}
if (!MultReadInProgress) { /* This prevents a race condition that could arise if the *interruptistriggeredwhilethecallingofthistimer *callbackfunctiontakesplace.TheIRQfunctionthenhas *alreadycleared'MultReadInProgress'whenflowofcontrol *getshere.
*/
local_irq_restore(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 *thereadoperation
*/
SET_IRQ_HANDLER( NULL );
MultReadInProgress = 0;
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);
}
}
staticvoid fd_rwsec_done( int status )
{
DPRINT(("fd_rwsec_done()\n"));
if (read_track) {
timer_delete(&readtrack_timer); if (!MultReadInProgress) return;
MultReadInProgress = 0;
}
fd_rwsec_done1(status);
}
MotorOn = 1;
start_timeout(); /* wait for interrupt */
}
staticvoid 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;
}
complete(&format_wait); return;
err_end:
fd_error();
}
staticvoid fd_times_out(struct timer_list *unused)
{
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)
timer_delete(&readtrack_timer);
FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
udelay( 25 );
/* The (noop) seek operation here is needed to make the WP bit in the *FDCstatusregisteraccessibleforcheck_change.Ifthelastdisk *operationwouldhavebeenaRDSEC,thisbitwouldalwaysreadas0 *nomatterwhat:-(Tosavetime,theseekgoestothetrackwe're *alreadyon.
*/
staticvoid finish_fdc( void )
{ if (!NeedSeek || !stdma_is_locked_by(floppy_irq)) {
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 isreleasedimmediatelyafterwardsandtheinterrupt
may be delivered to the wrong driver. */
}
}
staticvoid finish_fdc_done( int dummy )
{ unsignedlong flags;
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 *lastseekcommand,theWPbitstillreadswrong:-((
*/
mod_timer(&fd_timer, jiffies + 5); else
start_check_change_timer();
start_motor_off_timer();
local_irq_save(flags); if (stdma_is_locked_by(floppy_irq))
stdma_release();
local_irq_restore(flags);
DPRINT(("finish_fdc() finished\n"));
}
/* The detection of disk changes is a dark chapter in Atari history :-( *Becausethe"Driveready"signalisn'tpresentintheAtari *hardware,onehastorelyonthe"WriteProtect".Thisworksfine, *aslongasnowriteprotecteddisksareused.TOSsolvesthis *problembyintroducingtri-statelogic("maybechanged")and *lookingattheserialnumberinblock0.Thisisn'tpossiblefor *Linux,sincethefloppydrivercan'tmakeassumptionsaboutthe *filesystemusedonthediskandthusthecontentsofblock0.I've *chosenthemethodtoalwayssay"Thediskwaschanged"ifitis *unsurewhetheritwas.Thisimpliesthateveryopenormount *invalidatesthediskbuffersifyouworkwithwriteprotected *disks.Butatleastthisisbetterthanworkingwithincorrectdata *duetounrecogniseddiskchanges.
*/
staticunsignedint floppy_check_events(struct gendisk *disk, unsignedint clearing)
{ struct atari_floppy_struct *p = disk->private_data; unsignedint drive = p - unit; if (test_bit (drive, &fake_change)) { /* simulated change (e.g. after formatting) */ return DISK_EVENT_MEDIA_CHANGE;
} if (test_bit (drive, &changed_floppies)) { /* surely changed (the WP signal changed at least once) */ return DISK_EVENT_MEDIA_CHANGE;
} if (UD.wpstat) { /* WP is on -> could be changed: to be sure, buffers should be *invalidated...
*/ return DISK_EVENT_MEDIA_CHANGE;
}
switch (cmd) { case FDGETPRM: if (type) { if (--type >= NUM_DISK_MINORS) return -ENODEV; if (minor2disktype[type].drive_types > DriveType) return -ENODEV;
type = minor2disktype[type].index;
dtp = &atari_disk_type[type]; if (UD.flags & FTD_MSG)
printk (KERN_ERR "floppy%d: found dtp %p name %s!\n",
drive, dtp, dtp->name);
} else { if (!UDT) return -ENXIO; else
dtp = UDT;
}
memset((void *)&getprm, 0, sizeof(getprm));
getprm.size = dtp->blocks;
getprm.sect = dtp->spt;
getprm.head = 2;
getprm.track = dtp->blocks/dtp->spt/2;
getprm.stretch = dtp->stretch; if (copy_to_user(argp, &getprm, sizeof(getprm))) return -EFAULT; return0;
} switch (cmd) { case FDSETPRM: case FDDEFPRM: /* *MSch7/96:simple'setgeometry'case:justsetthe *'default'deviceparams(minor==0). *Currently,thedrivegeometryisclearedaftereach *diskchangeandsubsequentrevalidate()!simple *implementationofFDDEFPRM:savegeometryfroma *FDDEFPRMcallandrestoreitinfloppy_revalidate()!
*/
/* get the parameters from user space */ if (floppy->ref != 1 && floppy->ref != -1) return -EBUSY; if (copy_from_user(&setprm, argp, sizeof(setprm))) return -EFAULT; /* *firstofall:checkforfloppychangeandrevalidate, *orthenextaccesswillrevalidate-andclearUDT:-(
*/
if (floppy_check_events(disk, 0))
floppy_revalidate(disk);
if (cmd == FDDEFPRM) { /* save settings as permanent default type */
default_params[drive].name = dtp->name;
default_params[drive].spt = dtp->spt;
default_params[drive].blocks = dtp->blocks;
default_params[drive].fdc_speed = dtp->fdc_speed;
default_params[drive].stretch = dtp->stretch;
}
return0;
}
}
/* no matching disk type found above - setting user_params */
if (cmd == FDDEFPRM) { /* set permanent type */
dtp = &default_params[drive];
} else /* set user type (reset by disk change!) */
dtp = &user_params[drive];
UD.connected = 1;
UD.track = 0; switch( UserSteprate[drive] ) { case2:
UD.steprate = FDCSTEP_2; break; case3:
UD.steprate = FDCSTEP_3; break; case6:
UD.steprate = FDCSTEP_6; break; case12:
UD.steprate = FDCSTEP_12; break; default: /* should be -1 for "not set by user" */ if (ATARIHW_PRESENT( FDCSPEED ) || MACH_IS_MEDUSA)
UD.steprate = FDCSTEP_3; else
UD.steprate = FDCSTEP_6; break;
}
MotorOn = 1; /* from probe restore operation! */
}
/* This function tests the physical presence of a floppy drive (not *whetheradiskisinserted).Thisisdonebyissuingarestore *command,waitingmax.2seconds(thatshouldbeenoughtomovethe *headacrossthewholedisk)andlookingatthestateofthe"TR00" *signal.Thisshouldnowberaisedifthereisadriveconnected *(andthereisnohardwarefailure:-)Otherwise,thedriveis *declaredabsent.
*/
staticint __init fd_test_drive_present( int drive )
{ unsignedlong timeout; unsignedchar status; int ok;
if (FDC_READ( FDCREG_STATUS ) & FDCSTAT_BUSY) { /* If FDC is still busy from probing, give it another FORCI *commandtoaborttheoperation.Ifthisisn'tdone,theFDC *willinterruptlateranditsIRQlinestayslow,because *thestatusregisterisn'tread.Andthiswillblockany *interruptsonthisIRQline:-(
*/
FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
udelay(500);
FDC_READ( FDCREG_STATUS );
udelay(20);
}
if (cnt > 0) {
start_motor_off_timer(); if (cnt == 1) fd_select_drive( 0 );
start_check_change_timer();
}
}
staticvoid ataflop_probe(dev_t dev)
{ int drive = MINOR(dev) & 3; int type = MINOR(dev) >> 2;
if (type)
type--;
if (drive >= FD_MAX_UNITS || type >= NUM_DISK_MINORS) return; if (unit[drive].disk[type]) return; if (ataflop_alloc_disk(drive, type)) return; if (add_disk(unit[drive].disk[type])) goto cleanup_disk;
unit[drive].registered[type] = true; return;
staticvoid atari_floppy_cleanup(void)
{ int i; int type;
for (i = 0; i < FD_MAX_UNITS; i++) { for (type = 0; type < NUM_DISK_MINORS; type++) { if (!unit[i].disk[type]) continue;
del_gendisk(unit[i].disk[type]);
put_disk(unit[i].disk[type]);
}
blk_mq_free_tag_set(&unit[i].tag_set);
}
staticvoid atari_cleanup_floppy_disk(struct atari_floppy_struct *fs)
{ int type;
for (type = 0; type < NUM_DISK_MINORS; type++) { if (!fs->disk[type]) continue; if (fs->registered[type])
del_gendisk(fs->disk[type]);
put_disk(fs->disk[type]);
}
blk_mq_free_tag_set(&fs->tag_set);
}
staticint __init atari_floppy_init (void)
{ int i; int ret;
if (!MACH_IS_ATARI) /* Amiga, Mac, ... don't have Atari-compatible floppy :-) */ return -ENODEV;
for (i = 0; i < FD_MAX_UNITS; i++) {
memset(&unit[i].tag_set, 0, sizeof(unit[i].tag_set));
unit[i].tag_set.ops = &ataflop_mq_ops;
unit[i].tag_set.nr_hw_queues = 1;
unit[i].tag_set.nr_maps = 1;
unit[i].tag_set.queue_depth = 2;
unit[i].tag_set.numa_node = NUMA_NO_NODE;
ret = blk_mq_alloc_tag_set(&unit[i].tag_set); if (ret) goto err;
ret = ataflop_alloc_disk(i, 0); if (ret) {
blk_mq_free_tag_set(&unit[i].tag_set); goto err;
}
}
if (UseTrackbuffer < 0) /* not set by user -> use default: for now, we turn trackbufferingoffforallMedusas,thoughit couldbeusedwithonesthathaveacounter
card. But the test is too hard :-( */
UseTrackbuffer = !MACH_IS_MEDUSA;
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.58Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.