📄 sr.c
字号:
/* * 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 + -