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

📄 sr.c

📁 GNU Mach 微内核源代码, 基于美国卡内基美隆大学的 Mach 研究项目
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  sr.c Copyright (C) 1992 David Giller *	     Copyright (C) 1993, 1994, 1995 Eric Youngdale * *  adapted from: *	sd.c Copyright (C) 1992 Drew Eckhardt  *	Linux scsi disk driver by *		Drew Eckhardt <drew@colorado.edu> * *      Modified by Eric Youngdale ericy@cais.com to *      add scatter-gather, multiple outstanding request, and other *      enhancements. * *	    Modified by Eric Youngdale eric@aib.com to support loadable *	    low-level scsi drivers. * *	 Modified by Thomas Quinot thomas@melchior.cuivre.fdn.fr to *	 provide auto-eject. * */#include <linux/module.h>#include <linux/fs.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/cdrom.h>#include <linux/interrupt.h>#include <asm/system.h>#define MAJOR_NR SCSI_CDROM_MAJOR#include <linux/blk.h>#include "scsi.h"#include "hosts.h"#include "sr.h"#include <scsi/scsi_ioctl.h>   /* For the door lock/unlock commands */#include "constants.h"#define MAX_RETRIES 3#define SR_TIMEOUT (30 * HZ)static int sr_init(void);static void sr_finish(void);static int sr_attach(Scsi_Device *);static int sr_detect(Scsi_Device *);static void sr_detach(Scsi_Device *);struct Scsi_Device_Template sr_template = {NULL, "cdrom", "sr", NULL, TYPE_ROM, 					       SCSI_CDROM_MAJOR, 0, 0, 0, 1,					       sr_detect, sr_init,					       sr_finish, sr_attach, sr_detach};Scsi_CD * scsi_CDs = NULL;static int * sr_sizes;static int * sr_blocksizes;static int sr_open(struct inode *, struct file *);void get_sectorsize(int);void sr_photocd(struct inode *);extern int sr_ioctl(struct inode *, struct file *, unsigned int, unsigned long);void requeue_sr_request (Scsi_Cmnd * SCpnt);static int check_cdrom_media_change(kdev_t);static void sr_release(struct inode * inode, struct file * file){	sync_dev(inode->i_rdev);	if(! --scsi_CDs[MINOR(inode->i_rdev)].device->access_count)	{	    sr_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);	    if (scsi_CDs[MINOR(inode->i_rdev)].auto_eject)		sr_ioctl(inode, NULL, CDROMEJECT, 0);	}	if (scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)	    (*scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)--;	if(sr_template.usage_count) (*sr_template.usage_count)--;}static struct file_operations sr_fops = {	NULL,			/* lseek - default */	block_read,		/* read - general block-dev read */	block_write,		/* write - general block-dev write */	NULL,			/* readdir - bad */	NULL,			/* select */	sr_ioctl,		/* ioctl */	NULL,			/* mmap */	sr_open,       	/* special open code */	sr_release,		/* release */	NULL,			/* fsync */	NULL,			/* fasync */	check_cdrom_media_change,  /* Disk change */	NULL			/* revalidate */};/* * This function checks to see if the media has been changed in the * CDROM drive.  It is possible that we have already sensed a change, * or the drive may have sensed one and not yet reported it.  We must * be ready for either case. This function always reports the current * value of the changed bit.  If flag is 0, then the changed bit is reset. * This function could be done as an ioctl, but we would need to have * an inode for that to work, and we do not always have one. */int check_cdrom_media_change(kdev_t full_dev){	int retval, target;	struct inode inode;	int flag = 0;    	target =  MINOR(full_dev);    	if (target >= sr_template.nr_dev) {		printk("CD-ROM request error: invalid device.\n");		return 0;	};    	inode.i_rdev = full_dev;  /* This is all we really need here */	retval = sr_ioctl(&inode, NULL, SCSI_IOCTL_TEST_UNIT_READY, 0);    	if(retval){ /* Unable to test, unit probably not ready.  This usually		 * means there is no disc in the drive.  Mark as changed,		 * and we will figure it out later once the drive is		 * available again.  */		scsi_CDs[target].device->changed = 1;	return 1; /* This will force a flush, if called from		   * check_disk_change */	};    	retval = scsi_CDs[target].device->changed;	if(!flag) {	scsi_CDs[target].device->changed = 0;	/* If the disk changed, the capacity will now be different,	 * so we force a re-read of this information */	if (retval) scsi_CDs[target].needs_sector_size = 1;	};	return retval;}/* * rw_intr is the interrupt routine for the device driver.  It will be notified on the  * end of a SCSI read / write, and will take on of several actions based on success or failure. */static void rw_intr (Scsi_Cmnd * SCpnt){	int result = SCpnt->result;	int this_count = SCpnt->this_count;	int good_sectors = (result == 0 ? this_count : 0);	int block_sectors = 0;    #ifdef DEBUG	printk("sr.c done: %x %x\n",result, SCpnt->request.bh->b_data);#endif    /*      Handle MEDIUM ERRORs or VOLUME OVERFLOWs that indicate partial success.      Since this is a relatively rare error condition, no care is taken to      avoid unnecessary additional work such as memcpy's that could be avoided.    */	if (driver_byte(result) != 0 &&		    /* An error occurred */	    SCpnt->sense_buffer[0] == 0xF0 &&	    /* Sense data is valid */	    (SCpnt->sense_buffer[2] == MEDIUM_ERROR ||	     SCpnt->sense_buffer[2] == VOLUME_OVERFLOW ||	     SCpnt->sense_buffer[2] == ILLEGAL_REQUEST))	  {	    long error_sector = (SCpnt->sense_buffer[3] << 24) |				(SCpnt->sense_buffer[4] << 16) |				(SCpnt->sense_buffer[5] << 8) |				SCpnt->sense_buffer[6];	    int device_nr = DEVICE_NR(SCpnt->request.rq_dev);	    if (SCpnt->request.bh != NULL)	      block_sectors = SCpnt->request.bh->b_size >> 9;	    if (block_sectors < 4) block_sectors = 4;	    if (scsi_CDs[device_nr].sector_size == 2048)	      error_sector <<= 2;	    error_sector &= ~ (block_sectors - 1);	    good_sectors = error_sector - SCpnt->request.sector;	    if (good_sectors < 0 || good_sectors >= this_count)	      good_sectors = 0;	    /*	      The SCSI specification allows for the value returned by READ	      CAPACITY to be up to 75 2K sectors past the last readable	      block.  Therefore, if we hit a medium error within the last	      75 2K sectors, we decrease the saved size value.	    */	    if ((error_sector >> 1) < sr_sizes[device_nr] &&		scsi_CDs[device_nr].capacity - error_sector < 4*75)	      sr_sizes[device_nr] = error_sector >> 1;	  }	if (good_sectors > 0)    { /* Some sectors were read successfully. */	if (SCpnt->use_sg == 0) {		    if (SCpnt->buffer != SCpnt->request.buffer)	    {		int offset;		offset = (SCpnt->request.sector % 4) << 9;		memcpy((char *)SCpnt->request.buffer, 		       (char *)SCpnt->buffer + offset, 		       good_sectors << 9);		/* Even though we are not using scatter-gather, we look		 * ahead and see if there is a linked request for the		 * other half of this buffer.  If there is, then satisfy		 * it. */		if((offset == 0) && good_sectors == 2 &&		   SCpnt->request.nr_sectors > good_sectors && 		   SCpnt->request.bh &&		   SCpnt->request.bh->b_reqnext &&		   SCpnt->request.bh->b_reqnext->b_size == 1024) {		    memcpy((char *)SCpnt->request.bh->b_reqnext->b_data, 			   (char *)SCpnt->buffer + 1024, 			   1024);		    good_sectors += 2;		};				scsi_free(SCpnt->buffer, 2048);	    }	} else {		    struct scatterlist * sgpnt;		    int i;		    sgpnt = (struct scatterlist *) SCpnt->buffer;		    for(i=0; i<SCpnt->use_sg; i++) {		if (sgpnt[i].alt_address) {		    if (sgpnt[i].alt_address != sgpnt[i].address) {			memcpy(sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length);		    };		    scsi_free(sgpnt[i].address, sgpnt[i].length);		};		    };		    scsi_free(SCpnt->buffer, SCpnt->sglist_len);  /* Free list of scatter-gather pointers */		    if(SCpnt->request.sector % 4) good_sectors -= 2;	    /* See   if there is a padding record at the end that needs to be removed */		    if(good_sectors > SCpnt->request.nr_sectors)		good_sectors -= 2;	};	#ifdef DEBUG		printk("(%x %x %x) ",SCpnt->request.bh, SCpnt->request.nr_sectors, 		       good_sectors);#endif		if (SCpnt->request.nr_sectors > this_count)	{	 			SCpnt->request.errors = 0;			if (!SCpnt->request.bh)			    panic("sr.c: linked page request (%lx %x)",		      SCpnt->request.sector, this_count);	}		SCpnt = end_scsi_request(SCpnt, 1, good_sectors);  /* All done */	if (result == 0)	  {	    requeue_sr_request(SCpnt);	    return;	  }    }        if (good_sectors == 0) {	/* We only come through here if no sectors were read successfully. */        /* Free up any indirection buffers we allocated for DMA purposes. */	if (SCpnt->use_sg) {	struct scatterlist * sgpnt;	int i;	sgpnt = (struct scatterlist *) SCpnt->buffer;	for(i=0; i<SCpnt->use_sg; i++) {	    if (sgpnt[i].alt_address) {		scsi_free(sgpnt[i].address, sgpnt[i].length);	    }	}	scsi_free(SCpnt->buffer, SCpnt->sglist_len);  /* Free list of scatter-gather pointers */	} else {	if (SCpnt->buffer != SCpnt->request.buffer)	    scsi_free(SCpnt->buffer, SCpnt->bufflen);	}        }	if (driver_byte(result) != 0) {		if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70) {			if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) {				/* detected disc change.  set a bit and quietly refuse 				 * further access.	*/						scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->changed = 1;				SCpnt = end_scsi_request(SCpnt, 0, this_count);				requeue_sr_request(SCpnt);				return;			}		}	    		if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {			printk("CD-ROM error: ");			print_sense("sr", SCpnt);			printk("command was: ");			print_command(SCpnt->cmnd);			if (scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].ten) {				scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].ten = 0;				requeue_sr_request(SCpnt);				result = 0;				return;			} else {				SCpnt = end_scsi_request(SCpnt, 0, this_count);				requeue_sr_request(SCpnt); /* Do next request */				return;			}	    		}		if (SCpnt->sense_buffer[2] == NOT_READY) {			printk("CD-ROM not ready.  Make sure you have a disc in the drive.\n");			SCpnt = end_scsi_request(SCpnt, 0, this_count);			requeue_sr_request(SCpnt); /* Do next request */			return;		}		if (SCpnt->sense_buffer[2] == MEDIUM_ERROR) {		    printk("scsi%d: MEDIUM ERROR on "			   "channel %d, id %d, lun %d, CDB: ",			   SCpnt->host->host_no, (int) SCpnt->channel, 			   (int) SCpnt->target, (int) SCpnt->lun);		    print_command(SCpnt->cmnd);		    print_sense("sr", SCpnt);		    SCpnt = end_scsi_request(SCpnt, 0, block_sectors);		    requeue_sr_request(SCpnt);		    return;		}		if (SCpnt->sense_buffer[2] == VOLUME_OVERFLOW) {		    printk("scsi%d: VOLUME OVERFLOW on "			   "channel %d, id %d, lun %d, CDB: ",			   SCpnt->host->host_no, (int) SCpnt->channel, 			   (int) SCpnt->target, (int) SCpnt->lun);		    print_command(SCpnt->cmnd);		    print_sense("sr", SCpnt);		    SCpnt = end_scsi_request(SCpnt, 0, block_sectors);		    requeue_sr_request(SCpnt);		    return;		}    }		/* We only get this far if we have an error we have not recognized */	if(result) {	printk("SCSI CD error : host %d id %d lun %d return code = %03x\n", 	       scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->host->host_no, 	       scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->id,	       scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->lun,	       result);	    	if (status_byte(result) == CHECK_CONDITION)	    print_sense("sr", SCpnt);		SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors);	requeue_sr_request(SCpnt);    }}/* * Here I tried to implement support for multisession-CD's *  * Much of this has do be done with vendor-specific SCSI-commands, because * multisession is newer than the SCSI-II standard. * So I have to complete it step by step. Useful information is welcome. * * Actually works: *   - NEC:     Detection and support of multisession CD's. Special handling *              for XA-disks is not necessary. *      *   - TOSHIBA: setting density is done here now, mounting PhotoCD's should *              work now without running the program "set_density" *              Multisession CD's are supported too. * *   Gerd Knorr <kraxel@cs.tu-berlin.de>  *//* * 19950704 operator@melchior.cuivre.fdn.fr (Thomas Quinot) * *   - SONY:	Same as Nec. * *   - PIONEER: works with SONY code (may be others too ?) */void sr_photocd(struct inode *inode){    unsigned long   sector,min,sec,frame;    unsigned char   buf[40];    /* the buffer for the ioctl */    unsigned char   *cmd;       /* the scsi-command */    unsigned char   *send;      /* the data we send to the drive ... */    unsigned char   *rec;       /* ... and get back */    int             rc,is_xa,no_multi;        if (scsi_CDs[MINOR(inode->i_rdev)].xa_flags & 0x02) {#ifdef DEBUG	printk(KERN_DEBUG "sr_photocd: CDROM and/or driver do not support multisession CD's");#endif	return;    }        if (!suser()) {	/* I'm not the superuser, so SCSI_IOCTL_SEND_COMMAND isn't allowed         * for me. That's why mpcd_sector will be initialized with zero,         * because I'm not able to get the right value. Necessary only if         * access_count is 1, else no disk change happened since the last         * call of this function and we can keep the old value.	 */	if (1 == scsi_CDs[MINOR(inode->i_rdev)].device->access_count) {	    scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = 0;	    scsi_CDs[MINOR(inode->i_rdev)].xa_flags &= ~0x01;	}	return;    }        sector   = 0;    is_xa    = 0;    no_multi = 0;    cmd = rec = &buf[8];        switch(scsi_CDs[MINOR(inode->i_rdev)].device->manufacturer) {	    case SCSI_MAN_NEC:#ifdef DEBUG	printk(KERN_DEBUG "sr_photocd: use NEC code\n");#endif	memset(buf,0,40);	*((unsigned int*)buf)   = 0x0;   /* we send nothing...     */

⌨️ 快捷键说明

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