📄 sd.c
字号:
/* * sd.c Copyright (C) 1992 Drew Eckhardt * Copyright (C) 1993, 1994, 1995, 1999 Eric Youngdale * * Linux scsi disk driver * Initial versions: Drew Eckhardt * Subsequent revisions: Eric Youngdale * * <drew@colorado.edu> * * Modified by Eric Youngdale ericy@andante.org to * add scatter-gather, multiple outstanding request, and other * enhancements. * * Modified by Eric Youngdale eric@andante.org to support loadable * low-level scsi drivers. * * Modified by Jirka Hanika geo@ff.cuni.cz to support more * scsi disks using eight major numbers. * * Modified by Richard Gooch rgooch@atnf.csiro.au to support devfs. * * Modified by Torben Mathiasen tmm@image.dk * Resource allocation fixes in sd_init and cleanups. * * Modified by Alex Davis <letmein@erols.com> * Fix problem where partition info not being read in sd_open. * * Modified by Alex Davis <letmein@erols.com> * Fix problem where removable media could be ejected after sd_open. */#include <linux/config.h>#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/hdreg.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/smp.h>#include <asm/uaccess.h>#include <asm/system.h>#include <asm/io.h>#define MAJOR_NR SCSI_DISK0_MAJOR#include <linux/blk.h>#include <linux/blkpg.h>#include "scsi.h"#include "hosts.h"#include "sd.h"#include <scsi/scsi_ioctl.h>#include "constants.h"#include <scsi/scsicam.h> /* must follow "hosts.h" */#include <linux/genhd.h>/* * static const char RCSid[] = "$Header:"; */#define SD_MAJOR(i) (!(i) ? SCSI_DISK0_MAJOR : SCSI_DISK1_MAJOR-1+(i))#define SCSI_DISKS_PER_MAJOR 16#define SD_MAJOR_NUMBER(i) SD_MAJOR((i) >> 8)#define SD_MINOR_NUMBER(i) ((i) & 255)#define MKDEV_SD_PARTITION(i) MKDEV(SD_MAJOR_NUMBER(i), (i) & 255)#define MKDEV_SD(index) MKDEV_SD_PARTITION((index) << 4)#define N_USED_SCSI_DISKS (sd_template.dev_max + SCSI_DISKS_PER_MAJOR - 1)#define N_USED_SD_MAJORS (N_USED_SCSI_DISKS / SCSI_DISKS_PER_MAJOR)#define MAX_RETRIES 5/* * Time out in seconds for disks and Magneto-opticals (which are slower). */#define SD_TIMEOUT (30 * HZ)#define SD_MOD_TIMEOUT (75 * HZ)struct hd_struct *sd;static Scsi_Disk *rscsi_disks;static int *sd_sizes;static int *sd_blocksizes;static int *sd_hardsizes; /* Hardware sector size */static int *sd_max_sectors;static int check_scsidisk_media_change(kdev_t);static int fop_revalidate_scsidisk(kdev_t);static int sd_init_onedisk(int);static int sd_init(void);static void sd_finish(void);static int sd_attach(Scsi_Device *);static int sd_detect(Scsi_Device *);static void sd_detach(Scsi_Device *);static int sd_init_command(Scsi_Cmnd *);static struct Scsi_Device_Template sd_template = { name:"disk", tag:"sd", scsi_type:TYPE_DISK, major:SCSI_DISK0_MAJOR, /* * Secondary range of majors that this driver handles. */ min_major:SCSI_DISK1_MAJOR, max_major:SCSI_DISK7_MAJOR, blk:1, detect:sd_detect, init:sd_init, finish:sd_finish, attach:sd_attach, detach:sd_detach, init_command:sd_init_command,};static void rw_intr(Scsi_Cmnd * SCpnt);#if defined(CONFIG_PPC)/* * Moved from arch/ppc/pmac_setup.c. This is where it really belongs. */kdev_t __initsd_find_target(void *host, int tgt){ Scsi_Disk *dp; int i; for (dp = rscsi_disks, i = 0; i < sd_template.dev_max; ++i, ++dp) if (dp->device != NULL && dp->device->host == host && dp->device->id == tgt) return MKDEV_SD(i); return 0;}#endifstatic int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg){ kdev_t dev = inode->i_rdev; struct Scsi_Host * host; Scsi_Device * SDev; int diskinfo[4]; SDev = rscsi_disks[DEVICE_NR(dev)].device; if (!SDev) return -ENODEV; /* * If we are in the middle of error recovery, don't let anyone * else try and use this device. Also, if error recovery fails, it * may try and take the device offline, in which case all further * access to the device is prohibited. */ if( !scsi_block_when_processing_errors(SDev) ) { return -ENODEV; } switch (cmd) { case HDIO_GETGEO: /* Return BIOS disk parameters */ { struct hd_geometry *loc = (struct hd_geometry *) arg; if(!loc) return -EINVAL; host = rscsi_disks[DEVICE_NR(dev)].device->host; /* default to most commonly used values */ diskinfo[0] = 0x40; diskinfo[1] = 0x20; diskinfo[2] = rscsi_disks[DEVICE_NR(dev)].capacity >> 11; /* override with calculated, extended default, or driver values */ if(host->hostt->bios_param != NULL) host->hostt->bios_param(&rscsi_disks[DEVICE_NR(dev)], dev, &diskinfo[0]); else scsicam_bios_param(&rscsi_disks[DEVICE_NR(dev)], dev, &diskinfo[0]); if (put_user(diskinfo[0], &loc->heads) || put_user(diskinfo[1], &loc->sectors) || put_user(diskinfo[2], &loc->cylinders) || put_user(sd[SD_PARTITION(inode->i_rdev)].start_sect, &loc->start)) return -EFAULT; return 0; } case HDIO_GETGEO_BIG: { struct hd_big_geometry *loc = (struct hd_big_geometry *) arg; if(!loc) return -EINVAL; host = rscsi_disks[DEVICE_NR(dev)].device->host; /* default to most commonly used values */ diskinfo[0] = 0x40; diskinfo[1] = 0x20; diskinfo[2] = rscsi_disks[DEVICE_NR(dev)].capacity >> 11; /* override with calculated, extended default, or driver values */ if(host->hostt->bios_param != NULL) host->hostt->bios_param(&rscsi_disks[DEVICE_NR(dev)], dev, &diskinfo[0]); else scsicam_bios_param(&rscsi_disks[DEVICE_NR(dev)], dev, &diskinfo[0]); if (put_user(diskinfo[0], &loc->heads) || put_user(diskinfo[1], &loc->sectors) || put_user(diskinfo[2], (unsigned int *) &loc->cylinders) || put_user(sd[SD_PARTITION(inode->i_rdev)].start_sect, &loc->start)) return -EFAULT; return 0; } case BLKGETSIZE: case BLKGETSIZE64: case BLKROSET: case BLKROGET: case BLKRASET: case BLKRAGET: case BLKFLSBUF: case BLKSSZGET: case BLKPG: case BLKELVGET: case BLKELVSET: case BLKBSZGET: case BLKBSZSET: return blk_ioctl(inode->i_rdev, cmd, arg); case BLKRRPART: /* Re-read partition tables */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; return revalidate_scsidisk(dev, 1); default: return scsi_ioctl(rscsi_disks[DEVICE_NR(dev)].device , cmd, (void *) arg); }}static void sd_devname(unsigned int disknum, char *buffer){ if (disknum < 26) sprintf(buffer, "sd%c", 'a' + disknum); else { unsigned int min1; unsigned int min2; /* * For larger numbers of disks, we need to go to a new * naming scheme. */ min1 = disknum / 26; min2 = disknum % 26; sprintf(buffer, "sd%c%c", 'a' + min1 - 1, 'a' + min2); }}static request_queue_t *sd_find_queue(kdev_t dev){ Scsi_Disk *dpnt; int target; target = DEVICE_NR(dev); dpnt = &rscsi_disks[target]; if (!dpnt) return NULL; /* No such device */ return &dpnt->device->request_queue;}static int sd_init_command(Scsi_Cmnd * SCpnt){ int dev, devm, block, this_count; Scsi_Disk *dpnt;#if CONFIG_SCSI_LOGGING char nbuff[6];#endif devm = SD_PARTITION(SCpnt->request.rq_dev); dev = DEVICE_NR(SCpnt->request.rq_dev); block = SCpnt->request.sector; this_count = SCpnt->request_bufflen >> 9; SCSI_LOG_HLQUEUE(1, printk("Doing sd request, dev = %d, block = %d\n", devm, block)); dpnt = &rscsi_disks[dev]; if (devm >= (sd_template.dev_max << 4) || !dpnt || !dpnt->device->online || block + SCpnt->request.nr_sectors > sd[devm].nr_sects) { SCSI_LOG_HLQUEUE(2, printk("Finishing %ld sectors\n", SCpnt->request.nr_sectors)); SCSI_LOG_HLQUEUE(2, printk("Retry with 0x%p\n", SCpnt)); return 0; } block += sd[devm].start_sect; if (dpnt->device->changed) { /* * quietly refuse to do anything to a changed disc until the changed * bit has been reset */ /* printk("SCSI disk has been changed. Prohibiting further I/O.\n"); */ return 0; } SCSI_LOG_HLQUEUE(2, sd_devname(devm, nbuff)); SCSI_LOG_HLQUEUE(2, printk("%s : real dev = /dev/%d, block = %d\n", nbuff, dev, block)); /* * If we have a 1K hardware sectorsize, prevent access to single * 512 byte sectors. In theory we could handle this - in fact * the scsi cdrom driver must be able to handle this because * we typically use 1K blocksizes, and cdroms typically have * 2K hardware sectorsizes. Of course, things are simpler * with the cdrom, since it is read-only. For performance * reasons, the filesystems should be able to handle this * and not force the scsi disk driver to use bounce buffers * for this. */ if (dpnt->device->sector_size == 1024) { if ((block & 1) || (SCpnt->request.nr_sectors & 1)) { printk("sd.c:Bad block number requested"); return 0; } else { block = block >> 1; this_count = this_count >> 1; } } if (dpnt->device->sector_size == 2048) { if ((block & 3) || (SCpnt->request.nr_sectors & 3)) { printk("sd.c:Bad block number requested"); return 0; } else { block = block >> 2; this_count = this_count >> 2; } } if (dpnt->device->sector_size == 4096) { if ((block & 7) || (SCpnt->request.nr_sectors & 7)) { printk("sd.c:Bad block number requested"); return 0; } else { block = block >> 3; this_count = this_count >> 3; } } switch (SCpnt->request.cmd) { case WRITE: if (!dpnt->device->writeable) { return 0; } SCpnt->cmnd[0] = WRITE_6; SCpnt->sc_data_direction = SCSI_DATA_WRITE; break; case READ: SCpnt->cmnd[0] = READ_6; SCpnt->sc_data_direction = SCSI_DATA_READ; break; default: panic("Unknown sd command %d\n", SCpnt->request.cmd); } SCSI_LOG_HLQUEUE(2, printk("%s : %s %d/%ld 512 byte blocks.\n", nbuff, (SCpnt->request.cmd == WRITE) ? "writing" : "reading", this_count, SCpnt->request.nr_sectors)); SCpnt->cmnd[1] = (SCpnt->device->scsi_level <= SCSI_2) ? ((SCpnt->lun << 5) & 0xe0) : 0; if (((this_count > 0xff) || (block > 0x1fffff)) || SCpnt->device->ten) { if (this_count > 0xffff) this_count = 0xffff; SCpnt->cmnd[0] += READ_10 - READ_6; SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff; SCpnt->cmnd[3] = (unsigned char) (block >> 16) & 0xff; SCpnt->cmnd[4] = (unsigned char) (block >> 8) & 0xff; SCpnt->cmnd[5] = (unsigned char) block & 0xff; SCpnt->cmnd[6] = SCpnt->cmnd[9] = 0; SCpnt->cmnd[7] = (unsigned char) (this_count >> 8) & 0xff; SCpnt->cmnd[8] = (unsigned char) this_count & 0xff; } else { if (this_count > 0xff) this_count = 0xff; SCpnt->cmnd[1] |= (unsigned char) ((block >> 16) & 0x1f); SCpnt->cmnd[2] = (unsigned char) ((block >> 8) & 0xff); SCpnt->cmnd[3] = (unsigned char) block & 0xff; SCpnt->cmnd[4] = (unsigned char) this_count; SCpnt->cmnd[5] = 0; } /* * We shouldn't disconnect in the middle of a sector, so with a dumb * host adapter, it's safe to assume that we can at least transfer * this many bytes between each connect / disconnect. */ SCpnt->transfersize = dpnt->device->sector_size; SCpnt->underflow = this_count << 9; SCpnt->allowed = MAX_RETRIES; SCpnt->timeout_per_command = (SCpnt->device->type == TYPE_DISK ? SD_TIMEOUT : SD_MOD_TIMEOUT); /* * This is the completion routine we use. This is matched in terms * of capability to this function. */ SCpnt->done = rw_intr; /* * This indicates that the command is ready from our end to be * queued. */ return 1;}static int sd_open(struct inode *inode, struct file *filp){ int target, retval = -ENXIO; Scsi_Device * SDev; target = DEVICE_NR(inode->i_rdev); SCSI_LOG_HLQUEUE(1, printk("target=%d, max=%d\n", target, sd_template.dev_max)); if (target >= sd_template.dev_max || !rscsi_disks[target].device) return -ENXIO; /* No such device */ /* * If the device is in error recovery, wait until it is done. * If the device is offline, then disallow any access to it. */ if (!scsi_block_when_processing_errors(rscsi_disks[target].device)) { return -ENXIO; } /* * Make sure that only one process can do a check_change_disk at one time. * This is also used to lock out further access when the partition table * is being re-read. */ while (rscsi_disks[target].device->busy) { barrier(); cpu_relax(); } /* * The following code can sleep. * Module unloading must be prevented */ SDev = rscsi_disks[target].device; if (SDev->host->hostt->module) __MOD_INC_USE_COUNT(SDev->host->hostt->module); if (sd_template.module) __MOD_INC_USE_COUNT(sd_template.module); SDev->access_count++; if (rscsi_disks[target].device->removable) { SDev->allow_revalidate = 1; check_disk_change(inode->i_rdev); SDev->allow_revalidate = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -