📄 acsi.c
字号:
deltau=end_time.tv_usec - start_time.tv_usec; deltas=end_time.tv_sec - start_time.tv_sec; if (deltas > 1 || deltas < 0) return; if (deltas > 0) deltau += 1000*1000; if (deltau >= usec) return; udelay(usec-deltau);}/* acsicmd_dma() sends an ACSI command and sets up the DMA to transfer * 'blocks' blocks of 512 bytes from/to 'buffer'. * Because the _IRQ signal is used for handshaking the command bytes, * the ACSI interrupt has to be disabled in this function. If the end * of the operation should be signalled by a real interrupt, it has to be * reenabled afterwards. */static int acsicmd_dma( const char *cmd, char *buffer, int blocks, int rwflag, int enable){ unsigned long flags, paddr; int i;#ifdef NO_WRITE if (rwflag || *cmd == 0x0a) { printk( "ACSI: Write commands disabled!\n" ); return( 0 ); }#endif rwflag = rwflag ? 0x100 : 0; paddr = virt_to_phys( buffer ); acsi_delay_end(COMMAND_DELAY); DISABLE_IRQ(); local_irq_save(flags); /* Low on A1 */ dma_wd.dma_mode_status = 0x88 | rwflag; MFPDELAY(); /* set DMA address */ dma_wd.dma_lo = (unsigned char)paddr; paddr >>= 8; MFPDELAY(); dma_wd.dma_md = (unsigned char)paddr; paddr >>= 8; MFPDELAY(); 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); /* send the command bytes except the last */ for( i = 0; i < 5; ++i ) { DMA_LONG_WRITE( *cmd++, 0x8a | rwflag ); udelay(20); if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */ } /* Clear FIFO and switch DMA to correct direction */ dma_wd.dma_mode_status = 0x92 | (rwflag ^ 0x100); MFPDELAY(); dma_wd.dma_mode_status = 0x92 | rwflag; MFPDELAY(); /* How many sectors for DMA */ dma_wd.fdc_acces_seccount = blocks; MFPDELAY(); /* send last command byte */ dma_wd.dma_mode_status = 0x8a | rwflag; MFPDELAY(); DMA_LONG_WRITE( *cmd++, 0x0a | rwflag ); if (enable) ENABLE_IRQ(); udelay(80); return( 1 );}/* * acsicmd_nodma() sends an ACSI command that requires no DMA. */int acsicmd_nodma( const char *cmd, int enable){ int i; acsi_delay_end(COMMAND_DELAY); DISABLE_IRQ(); /* send first command byte */ dma_wd.dma_mode_status = 0x88; MFPDELAY(); DMA_LONG_WRITE( *cmd++, 0x8a ); udelay(20); if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */ /* send the intermediate command bytes */ for( i = 0; i < 4; ++i ) { DMA_LONG_WRITE( *cmd++, 0x8a ); udelay(20); if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */ } /* send last command byte */ DMA_LONG_WRITE( *cmd++, 0x0a ); if (enable) ENABLE_IRQ(); udelay(80); return( 1 ); /* Note that the ACSI interrupt is still disabled after this * function. If you want to get the IRQ delivered, enable it manually! */}static int acsi_reqsense( char *buffer, int targ, int lun){ CMDSET_TARG_LUN( reqsense_cmd, targ, lun); if (!acsicmd_dma( reqsense_cmd, buffer, 1, 0, 0 )) return( 0 ); if (!acsi_wait_for_IRQ( 10 )) return( 0 ); acsi_getstatus(); if (!acsicmd_nodma( reqsense_cmd, 0 )) return( 0 ); if (!acsi_wait_for_IRQ( 10 )) return( 0 ); acsi_getstatus(); if (!acsicmd_nodma( reqsense_cmd, 0 )) return( 0 ); if (!acsi_wait_for_IRQ( 10 )) return( 0 ); acsi_getstatus(); if (!acsicmd_nodma( reqsense_cmd, 0 )) return( 0 ); if (!acsi_wait_for_IRQ( 10 )) return( 0 ); acsi_getstatus(); dma_cache_maintenance( virt_to_phys(buffer), 16, 0 ); return( 1 );} /* * ACSI status phase: get the status byte from the bus * * I've seen several times that a 0xff status is read, propably due to * a timing error. In this case, the procedure is repeated after the * next _IRQ edge. */int acsi_getstatus( void ){ int status; DISABLE_IRQ(); for(;;) { if (!acsi_wait_for_IRQ( 100 )) { acsi_delay_start(); return( -1 ); } dma_wd.dma_mode_status = 0x8a; MFPDELAY(); status = dma_wd.fdc_acces_seccount; if (status != 0xff) break;#ifdef DEBUG printk("ACSI: skipping 0xff status byte\n" );#endif udelay(40); acsi_wait_for_noIRQ( 20 ); } dma_wd.dma_mode_status = 0x80; udelay(40); acsi_wait_for_noIRQ( 20 ); acsi_delay_start(); return( status & 0x1f ); /* mask of the device# */}#if (defined(CONFIG_ATARI_SLM) || defined(CONFIG_ATARI_SLM_MODULE))/* Receive data in an extended status phase. Needed by SLM printer. */int acsi_extstatus( char *buffer, int cnt ){ int status; DISABLE_IRQ(); udelay(80); while( cnt-- > 0 ) { if (!acsi_wait_for_IRQ( 40 )) return( 0 ); dma_wd.dma_mode_status = 0x8a; MFPDELAY(); status = dma_wd.fdc_acces_seccount; MFPDELAY(); *buffer++ = status & 0xff; udelay(40); } return( 1 );}/* Finish an extended status phase */void acsi_end_extstatus( void ){ dma_wd.dma_mode_status = 0x80; udelay(40); acsi_wait_for_noIRQ( 20 ); acsi_delay_start();}/* Send data in an extended command phase */int acsi_extcmd( unsigned char *buffer, int cnt ){ while( cnt-- > 0 ) { DMA_LONG_WRITE( *buffer++, 0x8a ); udelay(20); if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */ } return( 1 );}#endifstatic void acsi_print_error(const unsigned char *errblk, struct acsi_info_struct *aip){ int atari_err, i, errcode; struct acsi_error *arr; atari_err = aip->old_atari_disk; if (atari_err) errcode = errblk[0] & 0x7f; else if ((errblk[0] & 0x70) == 0x70) errcode = errblk[2] & 0x0f; else errcode = errblk[0] & 0x0f; printk( KERN_ERR "ACSI error 0x%02x", errcode ); if (errblk[0] & 0x80) printk( " for sector %d", ((errblk[1] & 0x1f) << 16) | (errblk[2] << 8) | errblk[0] ); arr = atari_err ? atari_acsi_errors : scsi_acsi_errors; i = atari_err ? sizeof(atari_acsi_errors)/sizeof(*atari_acsi_errors) : sizeof(scsi_acsi_errors)/sizeof(*scsi_acsi_errors); for( --i; i >= 0; --i ) if (arr[i].code == errcode) break; if (i >= 0) printk( ": %s\n", arr[i].text );}/******************************************************************* * * ACSI interrupt routine * Test, if this is a ACSI interrupt and call the irq handler * Otherwise ignore this interrupt. * *******************************************************************/static irqreturn_t acsi_interrupt(int irq, void *data, struct pt_regs *fp ){ void (*acsi_irq_handler)(void) = do_acsi; do_acsi = NULL; CLEAR_TIMER(); if (!acsi_irq_handler) acsi_irq_handler = unexpected_acsi_interrupt; acsi_irq_handler(); return IRQ_HANDLED;}/****************************************************************** * * The Interrupt handlers * *******************************************************************/static void unexpected_acsi_interrupt( void ){ printk( KERN_WARNING "Unexpected ACSI interrupt\n" );}/* This function is called in case of errors. Because we cannot reset * the ACSI bus or a single device, there is no other choice than * retrying several times :-( */static void bad_rw_intr( void ){ if (!CURRENT) return; if (++CURRENT->errors >= MAX_ERRORS) end_request(CURRENT, 0); /* Otherwise just retry */}static void read_intr( void ){ int status; status = acsi_getstatus(); if (status != 0) { struct gendisk *disk = CURRENT->rq_disk; struct acsi_info_struct *aip = disk->private_data; printk(KERN_ERR "%s: ", disk->disk_name); if (!acsi_reqsense(acsi_buffer, aip->target, aip->lun)) printk( "ACSI error and REQUEST SENSE failed (status=0x%02x)\n", status ); else { acsi_print_error(acsi_buffer, aip); if (CARTRCH_STAT(aip, acsi_buffer)) aip->changed = 1; } ENABLE_IRQ(); bad_rw_intr(); redo_acsi_request(); return; } dma_cache_maintenance( virt_to_phys(CurrentBuffer), CurrentNSect*512, 0 ); if (CurrentBuffer == acsi_buffer) copy_from_acsibuffer(); do_end_requests(); redo_acsi_request();}static void write_intr(void){ int status; status = acsi_getstatus(); if (status != 0) { struct gendisk *disk = CURRENT->rq_disk; struct acsi_info_struct *aip = disk->private_data; printk( KERN_ERR "%s: ", disk->disk_name); if (!acsi_reqsense( acsi_buffer, aip->target, aip->lun)) printk( "ACSI error and REQUEST SENSE failed (status=0x%02x)\n", status ); else { acsi_print_error(acsi_buffer, aip); if (CARTRCH_STAT(aip, acsi_buffer)) aip->changed = 1; } bad_rw_intr(); redo_acsi_request(); return; } do_end_requests(); redo_acsi_request();}static void acsi_times_out( unsigned long dummy ){ DISABLE_IRQ(); if (!do_acsi) return; do_acsi = NULL; printk( KERN_ERR "ACSI timeout\n" ); if (!CURRENT) return; if (++CURRENT->errors >= MAX_ERRORS) {#ifdef DEBUG printk( KERN_ERR "ACSI: too many errors.\n" );#endif end_request(CURRENT, 0); } redo_acsi_request();}/*********************************************************************** * * Scatter-gather utility functions * ***********************************************************************/static void copy_to_acsibuffer( void ){ int i; char *src, *dst; struct buffer_head *bh; src = CURRENT->buffer; dst = acsi_buffer; bh = CURRENT->bh; if (!bh) memcpy( dst, src, CurrentNSect*512 ); else for( i = 0; i < CurrentNReq; ++i ) { memcpy( dst, src, bh->b_size ); dst += bh->b_size; if ((bh = bh->b_reqnext)) src = bh->b_data; }}static void copy_from_acsibuffer( void ){ int i; char *src, *dst; struct buffer_head *bh; dst = CURRENT->buffer; src = acsi_buffer; bh = CURRENT->bh; if (!bh) memcpy( dst, src, CurrentNSect*512 ); else for( i = 0; i < CurrentNReq; ++i ) { memcpy( dst, src, bh->b_size ); src += bh->b_size; if ((bh = bh->b_reqnext)) dst = bh->b_data; }}static void do_end_requests( void ){ int i, n; if (!CURRENT->bh) { CURRENT->nr_sectors -= CurrentNSect; CURRENT->current_nr_sectors -= CurrentNSect; CURRENT->sector += CurrentNSect; if (CURRENT->nr_sectors == 0) end_request(CURRENT, 1); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -