📄 ide.c
字号:
del_timer(&hwgroup->timer); /* Is this needed?? */ if (hwgroup->poll_timeout != 0) { /* polling in progress? */ spin_unlock_irqrestore(&hwgroup->spinlock, flags); handler(drive); } else if (drive_is_ready(drive)) { printk("%s: lost interrupt\n", drive->name); spin_unlock_irqrestore(&hwgroup->spinlock, flags); handler(drive); } else { if (drive->waiting_for_dma) { (void) hwgroup->hwif->dmaproc(ide_dma_end, drive); printk("%s: timeout waiting for DMA\n", drive->name); /* * need something here for HX PIIX3 UDMA and HPT343.......AMH * irq timeout: status=0x58 { DriveReady SeekComplete DataRequest } */ } spin_unlock_irqrestore(&hwgroup->spinlock, flags); ide_error(drive, "irq timeout", GET_STAT()); } start_next_request(hwgroup, 0);}/* * There's nothing really useful we can do with an unexpected interrupt, * other than reading the status register (to clear it), and logging it. * There should be no way that an irq can happen before we're ready for it, * so we needn't worry much about losing an "important" interrupt here. * * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the * drive enters "idle", "standby", or "sleep" mode, so if the status looks * "good", we just ignore the interrupt completely. * * This routine assumes __cli() is in effect when called. * * If an unexpected interrupt happens on irq15 while we are handling irq14 * and if the two interfaces are "serialized" (CMD640), then it looks like * we could screw up by interfering with a new request being set up for irq15. * * In reality, this is a non-issue. The new command is not sent unless the * drive is ready to accept one, in which case we know the drive is not * trying to interrupt us. And ide_set_handler() is always invoked before * completing the issuance of any new drive command, so we will not be * accidently invoked as a result of any valid command completion interrupt. * */static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup){ byte stat; ide_hwif_t *hwif = hwgroup->hwif; /* * handle the unexpected interrupt */ do { if (hwif->irq == irq) { stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); if (!OK_STAT(stat, READY_STAT, BAD_STAT)) { /* Try to not flood the console with msgs */ static unsigned long last_msgtime = 0, count = 0; ++count; if (0 < (signed long)(jiffies - (last_msgtime + HZ))) { last_msgtime = jiffies; printk("%s%s: unexpected interrupt, status=0x%02x, count=%ld\n", hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat, count); } } } } while ((hwif = hwif->next) != hwgroup->hwif);}/* * entry point for all interrupts, caller does __cli() for us */void ide_intr (int irq, void *dev_id, struct pt_regs *regs){ unsigned long flags; ide_hwgroup_t *hwgroup = (ide_hwgroup_t *)dev_id; ide_hwif_t *hwif; ide_drive_t *drive; ide_handler_t *handler; __cli(); /* local CPU only */ spin_lock_irqsave(&hwgroup->spinlock, flags); hwif = hwgroup->hwif; if ((handler = hwgroup->handler) == NULL || hwgroup->poll_timeout != 0) { /* * Not expecting an interrupt from this drive. * That means this could be: * (1) an interrupt from another PCI device * sharing the same PCI INT# as us. * or (2) a drive just entered sleep or standby mode, * and is interrupting to let us know. * or (3) a spurious interrupt of unknown origin. * * For PCI, we cannot tell the difference, * so in that case we just ignore it and hope it goes away. */#ifdef CONFIG_BLK_DEV_IDEPCI if (IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL))#endif /* CONFIG_BLK_DEV_IDEPCI */ { /* * Probably not a shared PCI interrupt, * so we can safely try to do something about it: */ (void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]); unexpected_intr(irq, hwgroup); } spin_unlock_irqrestore(&hwgroup->spinlock, flags); return; } drive = hwgroup->drive; if (!drive || !drive_is_ready(drive)) { spin_unlock_irqrestore(&hwgroup->spinlock, flags); return; } hwgroup->handler = NULL; (void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]); del_timer(&(hwgroup->timer)); { struct request *rq; unsigned long block, sectors; if ((rq = hwgroup->rq) != NULL) { block = rq->sector; block += drive->part[MINOR(rq->rq_dev)&PARTN_MASK].start_sect + drive->sect0; sectors = drive->using_dma ? rq->nr_sectors : drive->mult_count ? drive->mult_count : 1; } } spin_unlock_irqrestore(&hwgroup->spinlock, flags); if (drive->unmask) ide__sti(); /* local CPU only */ handler(drive); /* service this interrupt, may set handler for next interrupt */ /* * Note that handler() may have set things up for another * interrupt to occur soon, but it cannot happen until * we exit from this routine, because it will be the * same irq as is currently being serviced here, * and Linux won't allow another (on any CPU) until we return. */ start_next_request(hwgroup, hwif->irq);}/* * get_info_ptr() returns the (ide_drive_t *) for a given device number. * It returns NULL if the given device number does not match any present drives. */static ide_drive_t *get_info_ptr (kdev_t i_rdev){ int major = MAJOR(i_rdev); unsigned int h; for (h = 0; h < MAX_HWIFS; ++h) { ide_hwif_t *hwif = &ide_hwifs[h]; if (hwif->present && major == hwif->major) { unsigned unit = DEVICE_NR(i_rdev); if (unit < MAX_DRIVES) { ide_drive_t *drive = &hwif->drives[unit]; if (drive->present) return drive; } break; } } return NULL;}/* * This function is intended to be used prior to invoking ide_do_drive_cmd(). */void ide_init_drive_cmd (struct request *rq){ rq->buffer = NULL; rq->cmd = IDE_DRIVE_CMD; rq->sector = 0; rq->nr_sectors = 0; rq->current_nr_sectors = 0; rq->sem = NULL; rq->bh = NULL; rq->bhtail = NULL; rq->next = NULL;}/* * This function issues a special IDE device request * onto the request queue. * * If action is ide_wait, then the rq is queued at the end of the * request queue, and the function sleeps until it has been processed. * This is for use when invoked from an ioctl handler. * * If action is ide_preempt, then the rq is queued at the head of * the request queue, displacing the currently-being-processed * request and this function returns immediately without waiting * for the new rq to be completed. This is VERY DANGEROUS, and is * intended for careful use by the ATAPI tape/cdrom driver code. * * If action is ide_next, then the rq is queued immediately after * the currently-being-processed-request (if any), and the function * returns without waiting for the new rq to be completed. As above, * This is VERY DANGEROUS, and is intended for careful use by the * ATAPI tape/cdrom driver code. * * If action is ide_end, then the rq is queued at the end of the * request queue, and the function returns immediately without waiting * for the new rq to be completed. This is again intended for careful * use by the ATAPI tape/cdrom driver code. */int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action){ unsigned long flags; ide_hwgroup_t *hwgroup = HWGROUP(drive); unsigned int major = HWIF(drive)->major; struct request *cur_rq; struct semaphore sem = MUTEX_LOCKED; if (IS_PDC4030_DRIVE && rq->buffer != NULL) return -ENOSYS; /* special drive cmds not supported */ rq->errors = 0; rq->rq_status = RQ_ACTIVE; rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS); if (action == ide_wait) rq->sem = &sem; spin_lock_irqsave(&io_request_lock, flags); cur_rq = drive->queue; if (cur_rq == NULL || action == ide_preempt) { rq->next = cur_rq; drive->queue = rq; if (action == ide_preempt) hwgroup->rq = NULL; } else { if (action == ide_wait || action == ide_end) { while (cur_rq->next != NULL) /* find end of list */ cur_rq = cur_rq->next; } rq->next = cur_rq->next; cur_rq->next = rq; } spin_unlock_irqrestore(&io_request_lock, flags); do_hwgroup_request(hwgroup); save_flags(flags); /* all CPUs; overkill? */ cli(); /* all CPUs; overkill? */ if (action == ide_wait && rq->rq_status != RQ_INACTIVE) down(&sem); /* wait for it to be serviced */ restore_flags(flags); /* all CPUs; overkill? */ return rq->errors ? -EIO : 0; /* return -EIO if errors */}/* * This routine is called to flush all partitions and partition tables * for a changed disk, and then re-read the new partition table. * If we are revalidating a disk because of a media change, then we * enter with usage == 0. If we are using an ioctl, we automatically have * usage == 1 (we need an open channel to use an ioctl :-), so this * is our limit. */int ide_revalidate_disk(kdev_t i_rdev){ ide_drive_t *drive; ide_hwgroup_t *hwgroup; unsigned int p, major, minor; long flags; if ((drive = get_info_ptr(i_rdev)) == NULL) return -ENODEV; major = MAJOR(i_rdev); minor = drive->select.b.unit << PARTN_BITS; hwgroup = HWGROUP(drive); spin_lock_irqsave(&hwgroup->spinlock, flags); if (drive->busy || (drive->usage > 1)) { spin_unlock_irqrestore(&hwgroup->spinlock, flags); return -EBUSY; }; drive->busy = 1; MOD_INC_USE_COUNT; spin_unlock_irqrestore(&hwgroup->spinlock, flags); for (p = 0; p < (1<<PARTN_BITS); ++p) { if (drive->part[p].nr_sects > 0) { kdev_t devp = MKDEV(major, minor+p); struct super_block * sb = get_super(devp); fsync_dev (devp); if (sb) invalidate_inodes(sb); invalidate_buffers (devp); set_blocksize(devp, 1024); } drive->part[p].start_sect = 0; drive->part[p].nr_sects = 0; }; drive->part[0].nr_sects = current_capacity(drive); if ((drive->media != ide_disk && drive->media != ide_floppy) || drive->driver == NULL || !drive->part[0].nr_sects) drive->part[0].start_sect = -1; resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit); drive->busy = 0; wake_up(&drive->wqueue); MOD_DEC_USE_COUNT; return 0;}static void revalidate_drives (void){ ide_hwif_t *hwif; ide_drive_t *drive; int index, unit; for (index = 0; index < MAX_HWIFS; ++index) { hwif = &ide_hwifs[index]; for (unit = 0; unit < MAX_DRIVES; ++unit) { drive = &ide_hwifs[index].drives[unit]; if (drive->revalidate) { drive->revalidate = 0; if (!initializing) (void) ide_revalidate_disk(MKDEV(hwif->major, unit<<PARTN_BITS)); } } }}static void ide_init_module (int type){ int found = 0; ide_module_t *module = ide_modules; while (module) { if (module->type == type) { found = 1; (void) module->init(); } module = module->next; } revalidate_drives();#ifdef CONFIG_KMOD if (!found && type == IDE_PROBE_MODULE) (void) request_module("ide-probe");#endif /* CONFIG_KMOD */}static int ide_open(struct inode * inode, struct file * filp){ ide_drive_t *drive; int rc; if ((drive = get_info_ptr(inode->i_rdev)) == NULL) return -ENXIO; MOD_INC_USE_COUNT; if (drive->driver == NULL) ide_init_module(IDE_DRIVER_MODULE);#ifdef CONFIG_KMOD if (drive->driver == NULL) { if (drive->media == ide_disk) (void) request_module("ide-disk"); if (drive->media == ide_cdrom) (void) request_module("ide-cd"); if (drive->media == ide_tape) (void) request_module("ide-tape"); if (drive->media == ide_floppy) (void) request_module("ide-floppy"); }#endif /* CONFIG_KMOD */ while (drive->busy) sleep_on(&drive->wqueue); drive->usage++; if (drive->driver != NULL) { if ((rc = DRIVER(drive)->open(inode, filp, drive))) MOD_DEC_USE_COUNT; return rc; } printk ("%s: driver not present\n", drive->name); drive->usage--; MOD_DEC_USE_COUNT; return -ENXIO;}/* * Releasing a block device means we sync() it, so that it can safely * be forgotten about... */static int ide_release(struct inode * inode, struct file * file){ ide_drive_t *drive; if ((drive = get_info_ptr(inode->i_rdev)) != NULL) { fsync_dev(inode->i_rdev); drive->usage--; if (drive->driver != NULL) DRIVER(drive)->release(inode, file, drive); MOD_DEC_USE_COUNT; } return 0;}int ide_replace_subdriver(ide_drive_t *drive, const char *driver){ if (!drive->present || drive->busy || drive->usage) goto abort; if (drive->driver != NULL && DRIVER(drive)->cleanup(drive)) goto abort; strncpy(drive->driver_req, driver, 9); ide_init_module(IDE_DRIVER_MODULE); drive->driver_req[0] = 0; ide_init_module(IDE_DRIVER_MODULE); if (DRIVER(drive) && !strcmp(DRIVER(drive)->name, driver)) return 0;abort: return 1;}void ide_unregister (unsigned int index){#ifdef OSKIT printk(KERN_CRIT "ide_unregister called\n");#else struct gendisk *gd, **gdp; ide_drive_t *drive, *d; ide_hwif_t *hwif, *g; ide_hwgroup_t *hwgroup; int irq_count = 0, unit, i; unsigned long flags; unsigned int p, minor; if (index >= MAX_HWIFS) return; save_flags(flags); /* all CPUs */ cli(); /* all CPUs */ hwif = &ide_hwifs[index]; if (!hwif->present) goto abort;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -