⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 acsi.c

📁 Linux块设备驱动源码
💻 C
📖 第 1 页 / 共 3 页
字号:
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);	}	else {		for( i = 0; i < CurrentNReq; ++i ) {			n = CURRENT->bh->b_size >> 9;			CURRENT->nr_sectors -= n;			CURRENT->current_nr_sectors -= n;			CURRENT->sector += n;			end_request(CURRENT, 1);		}	}}/*********************************************************************** * *  do_acsi_request and friends * ***********************************************************************/static void do_acsi_request( request_queue_t * q ){	stdma_lock( acsi_interrupt, NULL );	redo_acsi_request();}static void redo_acsi_request( void ){	unsigned			block, target, lun, nsect;	char 				*buffer;	unsigned long		pbuffer;	struct buffer_head	*bh;	struct gendisk *disk;	struct acsi_info_struct *aip;  repeat:	CLEAR_TIMER();	if (do_acsi)		return;	if (!CURRENT) {		do_acsi = NULL;		ENABLE_IRQ();		stdma_release();		return;	}	disk = CURRENT->rq_disk;	aip = disk->private_data;	if (CURRENT->bh) {		if (!CURRENT->bh && !buffer_locked(CURRENT->bh))			panic("ACSI: block not locked");	}	block = CURRENT->sector;	if (block+CURRENT->nr_sectors >= get_capacity(disk)) {#ifdef DEBUG		printk( "%s: attempted access for blocks %d...%ld past end of device at block %ld.\n",		       disk->disk_name,		       block, block + CURRENT->nr_sectors - 1,		       get_capacity(disk));#endif		end_request(CURRENT, 0);		goto repeat;	}	if (aip->changed) {		printk( KERN_NOTICE "%s: request denied because cartridge has "				"been changed.\n", disk->disk_name);		end_request(CURRENT, 0);		goto repeat;	}		target = aip->target;	lun    = aip->lun;	/* Find out how many sectors should be transferred from/to	 * consecutive buffers and thus can be done with a single command.	 */	buffer      = CURRENT->buffer;	pbuffer     = virt_to_phys(buffer);	nsect       = CURRENT->current_nr_sectors;	CurrentNReq = 1;	if ((bh = CURRENT->bh) && bh != CURRENT->bhtail) {		if (!STRAM_ADDR(pbuffer)) {			/* If transfer is done via the ACSI buffer anyway, we can			 * assemble as much bh's as fit in the buffer.			 */			while( (bh = bh->b_reqnext) ) {				if (nsect + (bh->b_size>>9) > ACSI_BUFFER_SECTORS) break;				nsect += bh->b_size >> 9;				++CurrentNReq;				if (bh == CURRENT->bhtail) break;			}			buffer = acsi_buffer;			pbuffer = phys_acsi_buffer;		}		else {			unsigned long pendadr, pnewadr;			pendadr = pbuffer + nsect*512;			while( (bh = bh->b_reqnext) ) {				pnewadr = virt_to_phys(bh->b_data);				if (!STRAM_ADDR(pnewadr) || pendadr != pnewadr) break;				nsect += bh->b_size >> 9;				pendadr = pnewadr + bh->b_size;				++CurrentNReq;				if (bh == CURRENT->bhtail) break;			}		}	}	else {		if (!STRAM_ADDR(pbuffer)) {			buffer = acsi_buffer;			pbuffer = phys_acsi_buffer;			if (nsect > ACSI_BUFFER_SECTORS)				nsect = ACSI_BUFFER_SECTORS;		}	}	CurrentBuffer = buffer;	CurrentNSect  = nsect;	if (rq_data_dir(CURRENT) == WRITE) {		CMDSET_TARG_LUN( write_cmd, target, lun );		CMDSET_BLOCK( write_cmd, block );		CMDSET_LEN( write_cmd, nsect );		if (buffer == acsi_buffer)			copy_to_acsibuffer();		dma_cache_maintenance( pbuffer, nsect*512, 1 );		do_acsi = write_intr;		if (!acsicmd_dma( write_cmd, buffer, nsect, 1, 1)) {			do_acsi = NULL;			printk( KERN_ERR "ACSI (write): Timeout in command block\n" );			bad_rw_intr();			goto repeat;		}		SET_TIMER();		return;	}	if (rq_data_dir(CURRENT) == READ) {		CMDSET_TARG_LUN( read_cmd, target, lun );		CMDSET_BLOCK( read_cmd, block );		CMDSET_LEN( read_cmd, nsect );		do_acsi = read_intr;		if (!acsicmd_dma( read_cmd, buffer, nsect, 0, 1)) {			do_acsi = NULL;			printk( KERN_ERR "ACSI (read): Timeout in command block\n" );			bad_rw_intr();			goto repeat;		}		SET_TIMER();		return;	}	panic("unknown ACSI command");}/*********************************************************************** * *  Misc functions: ioctl, open, release, check_change, ... * ***********************************************************************/static int acsi_ioctl( struct inode *inode, struct file *file,					   unsigned int cmd, unsigned long arg ){	struct gendisk *disk = inode->i_bdev->bd_disk;	struct acsi_info_struct *aip = disk->private_data;	switch (cmd) {	  case HDIO_GETGEO:		/* HDIO_GETGEO is supported more for getting the partition's		 * start sector... */	  { struct hd_geometry *geo = (struct hd_geometry *)arg;	    /* just fake some geometry here, it's nonsense anyway; to make it		 * easy, use Adaptec's usual 64/32 mapping */	    put_user( 64, &geo->heads );	    put_user( 32, &geo->sectors );	    put_user( aip->size >> 11, &geo->cylinders );		put_user(get_start_sect(inode->i_bdev), &geo->start);		return 0;	  }	  case SCSI_IOCTL_GET_IDLUN:		/* SCSI compatible GET_IDLUN call to get target's ID and LUN number */		put_user( aip->target | (aip->lun << 8),				  &((Scsi_Idlun *) arg)->dev_id );		put_user( 0, &((Scsi_Idlun *) arg)->host_unique_id );		return 0;	  default:		return -EINVAL;	}}/* * Open a device, check for read-only and lock the medium if it is * removable. * * Changes by Martin Rogge, 9th Aug 1995: * Check whether check_disk_change (and therefore revalidate_acsidisk) * was successful. They fail when there is no medium in the drive. * * The problem of media being changed during an operation can be  * ignored because of the prevent_removal code. * * Added check for the validity of the device number. * */static int acsi_open( struct inode * inode, struct file * filp ){	struct gendisk *disk = inode->i_bdev->bd_disk;	struct acsi_info_struct *aip = disk->private_data;	if (aip->access_count == 0 && aip->removable) {#if 0		aip->changed = 1;	/* safety first */#endif		check_disk_change( inode->i_bdev );		if (aip->changed)	/* revalidate was not successful (no medium) */			return -ENXIO;		acsi_prevent_removal(aip, 1);	}	aip->access_count++;	if (filp && filp->f_mode) {		check_disk_change( inode->i_bdev );		if (filp->f_mode & 2) {			if (aip->read_only) {				acsi_release( inode, filp );				return -EROFS;			}		}	}	return 0;}/* * Releasing a block device means we sync() it, so that it can safely * be forgotten about... */static int acsi_release( struct inode * inode, struct file * file ){	struct gendisk *disk = inode->i_bdev->bd_disk;	struct acsi_info_struct *aip = disk->private_data;	if (--aip->access_count == 0 && aip->removable)		acsi_prevent_removal(aip, 0);	return( 0 );}/* * Prevent or allow a media change for removable devices. */static void acsi_prevent_removal(struct acsi_info_struct *aip, int flag){	stdma_lock( NULL, NULL );		CMDSET_TARG_LUN(pa_med_rem_cmd, aip->target, aip->lun);	CMDSET_LEN( pa_med_rem_cmd, flag );		if (acsicmd_nodma(pa_med_rem_cmd, 0) && acsi_wait_for_IRQ(3*HZ))		acsi_getstatus();	/* Do not report errors -- some devices may not know this command. */	ENABLE_IRQ();	stdma_release();}static int acsi_media_change(struct gendisk *disk){	struct acsi_info_struct *aip = disk->private_data;	if (!aip->removable) 		return 0;	if (aip->changed)		/* We can be sure that the medium has been changed -- REQUEST		 * SENSE has reported this earlier.		 */		return 1;	/* If the flag isn't set, make a test by reading block 0.	 * If errors happen, it seems to be better to say "changed"...	 */	stdma_lock( NULL, NULL );	CMDSET_TARG_LUN(read_cmd, aip->target, aip->lun);	CMDSET_BLOCK( read_cmd, 0 );	CMDSET_LEN( read_cmd, 1 );	if (acsicmd_dma(read_cmd, acsi_buffer, 1, 0, 0) &&	    acsi_wait_for_IRQ(3*HZ)) {		if (acsi_getstatus()) {			if (acsi_reqsense(acsi_buffer, aip->target, aip->lun)) {				if (CARTRCH_STAT(aip, acsi_buffer))					aip->changed = 1;			}			else {				printk( KERN_ERR "%s: REQUEST SENSE failed in test for "				       "medium change; assuming a change\n", disk->disk_name );

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -