int ftape_report_operation(int *status, qic117_cmd_t command, int result_length) { int i, st3; unsigned int t0; unsigned int dt; TRACE_FUN(ft_t_any); TRACE_CATCH(ftape_command(command),); t0 = ftape_timestamp(); i = 0; do { ++i; ftape_sleep(3 * FT_MILLISECOND); /* see remark below */ TRACE_CATCH(fdc_sense_drive_status(&st3),); dt = ftape_timediff(t0, ftape_timestamp()); /* Ack should be asserted within Ttimout + Tack = 6 msec. * Looks like some drives fail to do this so extend this * period to 300 msec. */ } while (!(st3 & ST3_TRACK_0) && dt < 300000); if (!(st3 & ST3_TRACK_0)) { TRACE(ft_t_err, "No acknowledge after %u msec. (%i iter)", dt / 1000, i); TRACE_ABORT(-EIO, ft_t_err, "timeout on Acknowledge"); } /* dt may be larger than expected because of other tasks * scheduled while we were sleeping. */ if (i > 1 && dt > 6000) { TRACE(ft_t_err, "Acknowledge after %u msec. (%i iter)", dt / 1000, i); } *status = 0; for (i = 0; i < result_length + 1; i++) { TRACE_CATCH(ftape_command(QIC_REPORT_NEXT_BIT),); TRACE_CATCH(fdc_sense_drive_status(&st3),); if (i < result_length) { *status |= ((st3 & ST3_TRACK_0) ? 1 : 0) << i; } else if ((st3 & ST3_TRACK_0) == 0) { TRACE_ABORT(-EIO, ft_t_err, "missing status stop bit"); } } /* this command will put track zero and index back into normal state */ (void)ftape_command(QIC_REPORT_NEXT_BIT); TRACE_EXIT 0; }
/* Output a cmd_len long command string to the FDC. * The FDC should be ready to receive a new command or * an error (EBUSY or ETIME) will occur. */ int fdc_command(const __u8 * cmd_data, int cmd_len) { int result = 0; unsigned long flags; int count = cmd_len; int retry = 0; #ifdef TESTING static unsigned int last_time; unsigned int time; #endif TRACE_FUN(ft_t_any); fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */ spin_lock_irqsave(&fdc_io_lock, flags); if (!in_interrupt()) /* Yes, I know, too much comments inside this function * ... * * Yet another bug in the original driver. All that * havoc is caused by the fact that the isr() sends * itself a command to the floppy tape driver (pause, * micro step pause). Now, the problem is that * commands are transmitted via the fdc_seek * command. But: the fdc performs seeks in the * background i.e. it doesn't signal busy while * sending the step pulses to the drive. Therefore the * non-interrupt level driver has no chance to tell * whether the isr() just has issued a seek. Therefore * we HAVE TO have a look at the ft_hide_interrupt * flag: it signals the non-interrupt level part of * the driver that it has to wait for the fdc until it * has completet seeking. * * THIS WAS PRESUMABLY THE REASON FOR ALL THAT * "fdc_read timeout" errors, I HOPE :-) */ if (ft_hide_interrupt) { restore_flags(flags); TRACE(ft_t_info, "Waiting for the isr() completing fdc_seek()"); if (fdc_interrupt_wait(2 * FT_SECOND) < 0) { TRACE(ft_t_warn, "Warning: timeout waiting for isr() seek to complete"); } if (ft_hide_interrupt || !ft_seek_completed) { /* There cannot be another * interrupt. The isr() only stops * the tape and the next interrupt * won't come until we have send our * command to the drive. */ TRACE_ABORT(-EIO, ft_t_bug, "BUG? isr() is still seeking?\n" KERN_INFO "hide: %d\n" KERN_INFO "seek: %d", ft_hide_interrupt, ft_seek_completed); } fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */ spin_lock_irqsave(&fdc_io_lock, flags); } fdc_status = inb(fdc.msr); if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_IN_READY) { spin_unlock_irqrestore(&fdc_io_lock, flags); TRACE_ABORT(-EBUSY, ft_t_err, "fdc not ready"); } fdc_mode = *cmd_data; /* used by isr */ #ifdef TESTING if (fdc_mode == FDC_SEEK) { time = ftape_timediff(last_time, ftape_timestamp()); if (time < 6000) { TRACE(ft_t_bug,"Warning: short timeout between seek commands: %d", time); } } #endif if (!in_interrupt()) { /* shouldn't be cleared if called from isr */ ft_interrupt_seen = 0; } while (count) { result = fdc_write(*cmd_data); if (result < 0) { TRACE(ft_t_fdc_dma, "fdc_mode = %02x, status = %02x at index %d", (int) fdc_mode, (int) fdc_status, cmd_len - count); if (++retry <= 3) { TRACE(ft_t_warn, "fdc_write timeout, retry"); } else { TRACE(ft_t_err, "fdc_write timeout, fatal"); /* recover ??? */ break; } } else { --count; ++cmd_data; } } #ifdef TESTING if (fdc_mode == FDC_SEEK) { last_time = ftape_timestamp(); } #endif spin_unlock_irqrestore(&fdc_io_lock, flags); TRACE_EXIT result; }