📄 acsi.c
字号:
if (DEVICE_INTR) return; repeat: CLEAR_TIMER(); /* Another check here: An interrupt or timer event could have * happened since the last check! */ if (!QUEUE_EMPTY && CURRENT->rq_status == RQ_INACTIVE) { if (!DEVICE_INTR) { ENABLE_IRQ(); stdma_release(); } return; } if (DEVICE_INTR) return; if (QUEUE_EMPTY) { CLEAR_INTR; ENABLE_IRQ(); stdma_release(); return; } if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) panic(DEVICE_NAME ": request list destroyed"); if (CURRENT->bh) { if (!CURRENT->bh && !buffer_locked(CURRENT->bh)) panic(DEVICE_NAME ": block not locked"); } dev = MINOR(CURRENT->rq_dev); block = CURRENT->sector; if (DEVICE_NR(dev) >= NDevices || block+CURRENT->nr_sectors >= acsi_part[dev].nr_sects) {#ifdef DEBUG printk( "ad%c: attempted access for blocks %d...%ld past end of device at block %ld.\n", DEVICE_NR(dev)+'a', block, block + CURRENT->nr_sectors - 1, acsi_part[dev].nr_sects);#endif end_request(0); goto repeat; } if (acsi_info[DEVICE_NR(dev)].changed) { printk( KERN_NOTICE "ad%c: request denied because cartridge has " "been changed.\n", DEVICE_NR(dev)+'a' ); end_request(0); goto repeat; } block += acsi_part[dev].start_sect; target = acsi_info[DEVICE_NR(dev)].target; lun = acsi_info[DEVICE_NR(dev)].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 (CURRENT->cmd == 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 ); SET_INTR(write_intr); if (!acsicmd_dma( write_cmd, buffer, nsect, 1, 1)) { CLEAR_INTR; printk( KERN_ERR "ACSI (write): Timeout in command block\n" ); bad_rw_intr(); goto repeat; } SET_TIMER(); return; } if (CURRENT->cmd == READ) { CMDSET_TARG_LUN( read_cmd, target, lun ); CMDSET_BLOCK( read_cmd, block ); CMDSET_LEN( read_cmd, nsect ); SET_INTR(read_intr); if (!acsicmd_dma( read_cmd, buffer, nsect, 0, 1)) { CLEAR_INTR; 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 ){ int dev; if (!inode) return -EINVAL; dev = DEVICE_NR(MINOR(inode->i_rdev)); if (dev >= NDevices) return -EINVAL; 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( acsi_info[dev].size >> 11, &geo->cylinders ); put_user( acsi_part[MINOR(inode->i_rdev)].start_sect, &geo->start ); return 0; } case SCSI_IOCTL_GET_IDLUN: /* SCSI compatible GET_IDLUN call to get target's ID and LUN number */ put_user( acsi_info[dev].target | (acsi_info[dev].lun << 8), &((Scsi_Idlun *) arg)->dev_id ); put_user( 0, &((Scsi_Idlun *) arg)->host_unique_id ); return 0; case BLKGETSIZE: /* Return device size */ return put_user(acsi_part[MINOR(inode->i_rdev)].nr_sects, (long *) arg); case BLKROSET: case BLKROGET: case BLKFLSBUF: case BLKPG: return blk_ioctl(inode->i_rdev, cmd, arg); case BLKRRPART: /* Re-read partition tables */ if (!capable(CAP_SYS_ADMIN)) return -EACCES; return revalidate_acsidisk(inode->i_rdev, 1); 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 ){ int device; struct acsi_info_struct *aip; device = DEVICE_NR(MINOR(inode->i_rdev)); if (device >= NDevices) return -ENXIO; aip = &acsi_info[device]; while (busy[device]) sleep_on(&busy_wait); if (access_count[device] == 0 && aip->removable) {#if 0 aip->changed = 1; /* safety first */#endif check_disk_change( inode->i_rdev ); if (aip->changed) /* revalidate was not successful (no medium) */ return -ENXIO; acsi_prevent_removal(device, 1); } access_count[device]++; MOD_INC_USE_COUNT; if (filp && filp->f_mode) { check_disk_change( inode->i_rdev ); 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 ){ int device = DEVICE_NR(MINOR(inode->i_rdev)); if (--access_count[device] == 0 && acsi_info[device].removable) acsi_prevent_removal(device, 0); MOD_DEC_USE_COUNT; return( 0 );}/* * Prevent or allow a media change for removable devices. */static void acsi_prevent_removal(int device, int flag){ stdma_lock( NULL, NULL ); CMDSET_TARG_LUN(pa_med_rem_cmd, acsi_info[device].target, acsi_info[device].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 (dev_t dev){ int device = DEVICE_NR(MINOR(dev)); struct acsi_info_struct *aip; aip = &acsi_info[device]; 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(device, acsi_buffer)) aip->changed = 1; } else { printk( KERN_ERR "ad%c: REQUEST SENSE failed in test for " "medium change; assuming a change\n", device + 'a' ); aip->changed = 1; } } } else { printk( KERN_ERR "ad%c: Test for medium changed timed out; " "assuming a change\n", device + 'a'); aip->changed = 1; } ENABLE_IRQ(); stdma_release(); /* Now, after reading a block, the changed status is surely valid. */ return aip->changed;}static int acsi_change_blk_size( int target, int lun){ int i; for (i=0; i<12; i++) acsi_buffer[i] = 0; acsi_buffer[3] = 8; acsi_buffer[10] = 2; CMDSET_TARG_LUN( modeselect_cmd, target, lun); if (!acsicmd_dma( modeselect_cmd, acsi_buffer, 1,1,0) || !acsi_wait_for_IRQ( 3*HZ ) || acsi_getstatus() != 0 ) { return(0); } return(1);}static int acsi_mode_sense( int target, int lun, SENSE_DATA *sd ){ int page; CMDSET_TARG_LUN( modesense_cmd, target, lun ); for (page=0; page<4; page++) { modesense_cmd[2] = page; if (!acsicmd_dma( modesense_cmd, acsi_buffer, 1, 0, 0 ) || !acsi_wait_for_IRQ( 3*HZ ) || acsi_getstatus()) continue; /* read twice to jump over the second 16-byte border! */ udelay(300); if (acsi_wait_for_noIRQ( 20 ) && acsicmd_nodma( modesense_cmd, 0 ) && acsi_wait_for_IRQ( 3*HZ ) && acsi_getstatus() == 0) break; } if (page == 4) { return(0); } dma_cache_maintenance( phys_acsi_buffer, sizeof(SENSE_DATA), 0 ); *sd = *(SENSE_DATA *)acsi_buffer; /* Validity check, depending on type of data */ switch( SENSE_TYPE(*sd) ) { case SENSE_TYPE_ATARI: if (CAPACITY(*sd) == 0) goto invalid_sense; break; case SENSE_TYPE_SCSI: if (sd->scsi.descriptor_size != 8) goto invalid_sense; break; case SENSE_TYPE_UNKNOWN: printk( KERN_ERR "ACSI target %d, lun %d: Cannot interpret " "sense data\n", target, lun ); invalid_sense:#ifdef DEBUG { int i; printk( "Mode sense data for ACSI target %d, lun %d seem not valid:", target, lun ); for( i = 0; i < sizeof(SENSE_DATA); ++i ) printk( "%02x ", (unsigned char)acsi_buffer[i] ); printk( "\n" ); }#endif return( 0 ); } return( 1 );}/******************************************************************* * * Initialization * ********************************************************************/extern struct block_device_operations acsi_fops;static struct gendisk acsi_gendisk = { MAJOR_NR, /* Major number */ "ad", /* Major name */ 4, /* Bits to shift to get real from partition */ 1 << 4, /* Number of partitions per real */ acsi_part, /* hd struct */ acsi_sizes, /* block sizes */ 0, /* number */ (void *)acsi_info, /* internal */ NULL, /* next */ &acsi_fops, /* file operations */}; #define MAX_SCSI_DEVICE_CODE 10static const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] ={ "Direct-Access ", "Sequential-Access", "Printer ", "Processor ", "WORM ", "CD-ROM ", "Scanner ", "Optical Device ", "Medium Changer ", "Communications "};static void print_inquiry(unsigned char *data){ int i; printk(KERN_INFO " Vendor: "); for (i = 8; i < 16; i++) { if (data[i] >= 0x20 && i < data[4] + 5) printk("%c", data[i]); else printk(" "); } printk(" Model: "); for (i = 16; i < 32; i++) { if (data[i] >= 0x20 && i < data[4] + 5) printk("%c", data[i]); else printk(" "); } printk(" Rev: "); for (i = 32; i < 36; i++) { if (data[i] >= 0x20 && i < data[4] + 5) printk("%c", data[i]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -