📄 pd.c
字号:
static int pd_next_buf(void){ unsigned long saved_flags; pd_count--; pd_run--; pd_buf += 512; pd_block++; if (!pd_run) return 1; if (pd_count) return 0; spin_lock_irqsave(&pd_lock, saved_flags); end_request(pd_req, 1); pd_count = pd_req->current_nr_sectors; pd_buf = pd_req->buffer; spin_unlock_irqrestore(&pd_lock, saved_flags); return 0;}static unsigned long pd_timeout;static enum action do_pd_read_start(void){ if (pd_wait_for(pd_current, STAT_READY, "do_pd_read") & STAT_ERR) { if (pd_retries < PD_MAX_RETRIES) { pd_retries++; return Wait; } return Fail; } pd_ide_command(pd_current, IDE_READ, pd_block, pd_run); phase = do_pd_read_drq; pd_timeout = jiffies + PD_TMO; return Hold;}static enum action do_pd_write_start(void){ if (pd_wait_for(pd_current, STAT_READY, "do_pd_write") & STAT_ERR) { if (pd_retries < PD_MAX_RETRIES) { pd_retries++; return Wait; } return Fail; } pd_ide_command(pd_current, IDE_WRITE, pd_block, pd_run); while (1) { if (pd_wait_for(pd_current, STAT_DRQ, "do_pd_write_drq") & STAT_ERR) { if (pd_retries < PD_MAX_RETRIES) { pd_retries++; return Wait; } return Fail; } pi_write_block(pd_current->pi, pd_buf, 512); if (pd_next_buf()) break; } phase = do_pd_write_done; pd_timeout = jiffies + PD_TMO; return Hold;}static inline int pd_ready(void){ return !(status_reg(pd_current) & STAT_BUSY);}static enum action do_pd_read_drq(void){ if (!pd_ready() && !time_after_eq(jiffies, pd_timeout)) return Hold; while (1) { if (pd_wait_for(pd_current, STAT_DRQ, "do_pd_read_drq") & STAT_ERR) { if (pd_retries < PD_MAX_RETRIES) { pd_retries++; phase = do_pd_read_start; return Wait; } return Fail; } pi_read_block(pd_current->pi, pd_buf, 512); if (pd_next_buf()) break; } return Ok;}static enum action do_pd_write_done(void){ if (!pd_ready() && !time_after_eq(jiffies, pd_timeout)) return Hold; if (pd_wait_for(pd_current, STAT_READY, "do_pd_write_done") & STAT_ERR) { if (pd_retries < PD_MAX_RETRIES) { pd_retries++; phase = do_pd_write_start; return Wait; } return Fail; } return Ok;}/* special io requests *//* According to the ATA standard, the default CHS geometry should be available following a reset. Some Western Digital drives come up in a mode where only LBA addresses are accepted until the device parameters are initialised.*/static void pd_init_dev_parms(struct pd_unit *disk){ pd_wait_for(disk, 0, DBMSG("before init_dev_parms")); pd_send_command(disk, disk->sectors, 0, disk->heads - 1, 0, 0, IDE_INIT_DEV_PARMS); udelay(300); pd_wait_for(disk, 0, "Initialise device parameters");}static enum action pd_door_lock(struct pd_unit *disk){ if (!(pd_wait_for(disk, STAT_READY, "Lock") & STAT_ERR)) { pd_send_command(disk, 1, 0, 0, 0, 0, IDE_DOORLOCK); pd_wait_for(disk, STAT_READY, "Lock done"); } return Ok;}static enum action pd_door_unlock(struct pd_unit *disk){ if (!(pd_wait_for(disk, STAT_READY, "Lock") & STAT_ERR)) { pd_send_command(disk, 1, 0, 0, 0, 0, IDE_DOORUNLOCK); pd_wait_for(disk, STAT_READY, "Lock done"); } return Ok;}static enum action pd_eject(struct pd_unit *disk){ pd_wait_for(disk, 0, DBMSG("before unlock on eject")); pd_send_command(disk, 1, 0, 0, 0, 0, IDE_DOORUNLOCK); pd_wait_for(disk, 0, DBMSG("after unlock on eject")); pd_wait_for(disk, 0, DBMSG("before eject")); pd_send_command(disk, 0, 0, 0, 0, 0, IDE_EJECT); pd_wait_for(disk, 0, DBMSG("after eject")); return Ok;}static enum action pd_media_check(struct pd_unit *disk){ int r = pd_wait_for(disk, STAT_READY, DBMSG("before media_check")); if (!(r & STAT_ERR)) { pd_send_command(disk, 1, 1, 0, 0, 0, IDE_READ_VRFY); r = pd_wait_for(disk, STAT_READY, DBMSG("RDY after READ_VRFY")); } else disk->changed = 1; /* say changed if other error */ if (r & ERR_MC) { disk->changed = 1; pd_send_command(disk, 1, 0, 0, 0, 0, IDE_ACKCHANGE); pd_wait_for(disk, STAT_READY, DBMSG("RDY after ACKCHANGE")); pd_send_command(disk, 1, 1, 0, 0, 0, IDE_READ_VRFY); r = pd_wait_for(disk, STAT_READY, DBMSG("RDY after VRFY")); } return Ok;}static void pd_standby_off(struct pd_unit *disk){ pd_wait_for(disk, 0, DBMSG("before STANDBY")); pd_send_command(disk, 0, 0, 0, 0, 0, IDE_STANDBY); pd_wait_for(disk, 0, DBMSG("after STANDBY"));}static enum action pd_identify(struct pd_unit *disk){ int j; char id[PD_ID_LEN + 1];/* WARNING: here there may be dragons. reset() applies to both drives, but we call it only on probing the MASTER. This should allow most common configurations to work, but be warned that a reset can clear settings on the SLAVE drive.*/ if (disk->drive == 0) pd_reset(disk); write_reg(disk, 6, DRIVE(disk)); pd_wait_for(disk, 0, DBMSG("before IDENT")); pd_send_command(disk, 1, 0, 0, 0, 0, IDE_IDENTIFY); if (pd_wait_for(disk, STAT_DRQ, DBMSG("IDENT DRQ")) & STAT_ERR) return Fail; pi_read_block(disk->pi, pd_scratch, 512); disk->can_lba = pd_scratch[99] & 2; disk->sectors = le16_to_cpu(*(u16 *) (pd_scratch + 12)); disk->heads = le16_to_cpu(*(u16 *) (pd_scratch + 6)); disk->cylinders = le16_to_cpu(*(u16 *) (pd_scratch + 2)); if (disk->can_lba) disk->capacity = le32_to_cpu(*(u32 *) (pd_scratch + 120)); else disk->capacity = disk->sectors * disk->heads * disk->cylinders; for (j = 0; j < PD_ID_LEN; j++) id[j ^ 1] = pd_scratch[j + PD_ID_OFF]; j = PD_ID_LEN - 1; while ((j >= 0) && (id[j] <= 0x20)) j--; j++; id[j] = 0; disk->removable = pd_scratch[0] & 0x80; printk("%s: %s, %s, %d blocks [%dM], (%d/%d/%d), %s media\n", disk->name, id, disk->drive ? "slave" : "master", disk->capacity, disk->capacity / 2048, disk->cylinders, disk->heads, disk->sectors, disk->removable ? "removable" : "fixed"); if (disk->capacity) pd_init_dev_parms(disk); if (!disk->standby) pd_standby_off(disk); return Ok;}/* end of io request engine */static void do_pd_request(request_queue_t * q){ if (pd_req) return; pd_req = elv_next_request(q); if (!pd_req) return; schedule_fsm();}static int pd_special_command(struct pd_unit *disk, enum action (*func)(struct pd_unit *disk)){ DECLARE_COMPLETION(wait); struct request rq; int err = 0; memset(&rq, 0, sizeof(rq)); rq.errors = 0; rq.rq_status = RQ_ACTIVE; rq.rq_disk = disk->gd; rq.ref_count = 1; rq.waiting = &wait; blk_insert_request(disk->gd->queue, &rq, 0, func, 0); wait_for_completion(&wait); rq.waiting = NULL; if (rq.errors) err = -EIO; blk_put_request(&rq); return err;}/* kernel glue structures */static int pd_open(struct inode *inode, struct file *file){ struct pd_unit *disk = inode->i_bdev->bd_disk->private_data; disk->access++; if (disk->removable) { pd_special_command(disk, pd_media_check); pd_special_command(disk, pd_door_lock); } return 0;}static int pd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct pd_unit *disk = inode->i_bdev->bd_disk->private_data; struct hd_geometry __user *geo = (struct hd_geometry __user *) arg; struct hd_geometry g; switch (cmd) { case CDROMEJECT: if (disk->access == 1) pd_special_command(disk, pd_eject); return 0; case HDIO_GETGEO: if (disk->alt_geom) { g.heads = PD_LOG_HEADS; g.sectors = PD_LOG_SECTS; g.cylinders = disk->capacity / (g.heads * g.sectors); } else { g.heads = disk->heads; g.sectors = disk->sectors; g.cylinders = disk->cylinders; } g.start = get_start_sect(inode->i_bdev); if (copy_to_user(geo, &g, sizeof(struct hd_geometry))) return -EFAULT; return 0; default: return -EINVAL; }}static int pd_release(struct inode *inode, struct file *file){ struct pd_unit *disk = inode->i_bdev->bd_disk->private_data; if (!--disk->access && disk->removable) pd_special_command(disk, pd_door_unlock); return 0;}static int pd_check_media(struct gendisk *p){ struct pd_unit *disk = p->private_data; int r; if (!disk->removable) return 0; pd_special_command(disk, pd_media_check); r = disk->changed; disk->changed = 0; return r;}static int pd_revalidate(struct gendisk *p){ struct pd_unit *disk = p->private_data; if (pd_special_command(disk, pd_identify) == 0) set_capacity(p, disk->capacity); else set_capacity(p, 0); return 0;}static struct block_device_operations pd_fops = { .owner = THIS_MODULE, .open = pd_open, .release = pd_release, .ioctl = pd_ioctl, .media_changed = pd_check_media, .revalidate_disk= pd_revalidate};/* probing */static void pd_probe_drive(struct pd_unit *disk){ struct gendisk *p = alloc_disk(1 << PD_BITS); if (!p) return; strcpy(p->disk_name, disk->name); p->fops = &pd_fops; p->major = major; p->first_minor = (disk - pd) << PD_BITS; disk->gd = p; p->private_data = disk; p->queue = pd_queue; if (disk->drive == -1) { for (disk->drive = 0; disk->drive <= 1; disk->drive++) if (pd_special_command(disk, pd_identify) == 0) return; } else if (pd_special_command(disk, pd_identify) == 0) return; disk->gd = NULL; put_disk(p);}static int pd_detect(void){ int found = 0, unit, pd_drive_count = 0; struct pd_unit *disk; for (unit = 0; unit < PD_UNITS; unit++) { int *parm = *drives[unit]; struct pd_unit *disk = pd + unit; disk->pi = &disk->pia; disk->access = 0; disk->changed = 1; disk->capacity = 0; disk->drive = parm[D_SLV]; snprintf(disk->name, PD_NAMELEN, "%s%c", name, 'a'+unit); disk->alt_geom = parm[D_GEO]; disk->standby = parm[D_SBY]; if (parm[D_PRT]) pd_drive_count++; } if (pd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */ disk = pd; if (pi_init(disk->pi, 1, -1, -1, -1, -1, -1, pd_scratch, PI_PD, verbose, disk->name)) { pd_probe_drive(disk); if (!disk->gd) pi_release(disk->pi); } } else { for (unit = 0, disk = pd; unit < PD_UNITS; unit++, disk++) { int *parm = *drives[unit]; if (!parm[D_PRT]) continue; if (pi_init(disk->pi, 0, parm[D_PRT], parm[D_MOD], parm[D_UNI], parm[D_PRO], parm[D_DLY], pd_scratch, PI_PD, verbose, disk->name)) { pd_probe_drive(disk); if (!disk->gd) pi_release(disk->pi); } } } for (unit = 0, disk = pd; unit < PD_UNITS; unit++, disk++) { if (disk->gd) { set_capacity(disk->gd, disk->capacity); add_disk(disk->gd); found = 1; } } if (!found) printk("%s: no valid drive found\n", name); return found;}static int __init pd_init(void){ if (disable) goto out1; pd_queue = blk_init_queue(do_pd_request, &pd_lock); if (!pd_queue) goto out1; blk_queue_max_sectors(pd_queue, cluster); if (register_blkdev(major, name)) goto out2; printk("%s: %s version %s, major %d, cluster %d, nice %d\n", name, name, PD_VERSION, major, cluster, nice); if (!pd_detect()) goto out3; return 0;out3: unregister_blkdev(major, name);out2: blk_cleanup_queue(pd_queue);out1: return -ENODEV;}static void __exit pd_exit(void){ struct pd_unit *disk; int unit; unregister_blkdev(major, name); for (unit = 0, disk = pd; unit < PD_UNITS; unit++, disk++) { struct gendisk *p = disk->gd; if (p) { disk->gd = NULL; del_gendisk(p); put_disk(p); pi_release(disk->pi); } } blk_cleanup_queue(pd_queue);}MODULE_LICENSE("GPL");module_init(pd_init)module_exit(pd_exit)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -