📄 mfmhd.c
字号:
}static void do_mfm_request(request_queue_t *q){ DBG("do_mfm_request: about to mfm_request\n"); mfm_request();}static void mfm_interrupt_handler(int unused, void *dev_id, struct pt_regs *regs){ void (*handler) (void) = do_mfm; do_mfm = NULL; DBG("mfm_interrupt_handler (handler=0x%p)\n", handler); mfm_status = inw(MFM_STATUS); /* If CPR (Command Parameter Reject) and not busy it means that the command has some return message to give us */ if ((mfm_status & (STAT_CPR | STAT_BSY)) == STAT_CPR) { int len = 0; while (len < 16) { int in; in = inw(MFM_DATAIN); result[len++] = in >> 8; result[len++] = in; } } if (handler) { handler(); return; } outw (CMD_RCAL, MFM_COMMAND); /* Clear interrupt condition */ printk ("mfm: unexpected interrupt - status = "); print_status (); while (1);}/* * Tell the user about the drive if we decided it exists. */static void mfm_geometry(int drive){ struct mfm_info *p = mfm_info + drive; struct gendisk *disk = mfm_gendisk[drive]; disk->private_data = p; if (p->cylinders) printk ("%s: %dMB CHS=%d/%d/%d LCC=%d RECOMP=%d\n", disk->disk_name, p->cylinders * p->heads * p->sectors / 4096, p->cylinders, p->heads, p->sectors, p->lowcurrent, p->precomp); set_capacity(disk, p->cylinders * p->heads * p->sectors / 2);}#ifdef CONFIG_BLK_DEV_MFM_AUTODETECT/* * Attempt to detect a drive and find its geometry. The drive has already been * specified... * * We first recalibrate the disk, then try to probe sectors, heads and then * cylinders. NOTE! the cylinder probe may break drives. The xd disk driver * does something along these lines, so I assume that most drives are up to * this mistreatment... */static int mfm_detectdrive (int drive){ unsigned int mingeo[3], maxgeo[3]; unsigned int attribute, need_recal = 1; unsigned char cmdb[8]; memset (mingeo, 0, sizeof (mingeo)); maxgeo[0] = mfm_info[drive].sectors; maxgeo[1] = mfm_info[drive].heads; maxgeo[2] = mfm_info[drive].cylinders; cmdb[0] = drive + 1; cmdb[6] = 0; cmdb[7] = 1; for (attribute = 0; attribute < 3; attribute++) { while (mingeo[attribute] != maxgeo[attribute]) { unsigned int variable; variable = (maxgeo[attribute] + mingeo[attribute]) >> 1; cmdb[1] = cmdb[2] = cmdb[3] = cmdb[4] = cmdb[5] = 0; if (need_recal) { int tries = 5; do { issue_command (CMD_RCLB, cmdb, 2); wait_for_completion (); wait_for_command_end (); if (result[1] == 0x20) break; } while (result[1] && --tries); if (result[1]) { outw (CMD_RCAL, MFM_COMMAND); return 0; } need_recal = 0; } switch (attribute) { case 0: cmdb[5] = variable; issue_command (CMD_CMPD, cmdb, 8); break; case 1: cmdb[1] = variable; cmdb[4] = variable; issue_command (CMD_CMPD, cmdb, 8); break; case 2: cmdb[2] = variable >> 8; cmdb[3] = variable; issue_command (CMD_SEK, cmdb, 4); break; } wait_for_completion (); wait_for_command_end (); switch (result[1]) { case 0x00: case 0x50: mingeo[attribute] = variable + 1; break; case 0x20: outw (CMD_RCAL, MFM_COMMAND); return 0; case 0x24: need_recal = 1; default: maxgeo[attribute] = variable; break; } } } mfm_info[drive].cylinders = mingeo[2]; mfm_info[drive].lowcurrent = mingeo[2]; mfm_info[drive].precomp = mingeo[2] / 2; mfm_info[drive].heads = mingeo[1]; mfm_info[drive].sectors = mingeo[0]; outw (CMD_RCAL, MFM_COMMAND); return 1;}#endif/* * Initialise all drive information for this controller. */static int mfm_initdrives(void){ int drive; if (number_mfm_drives > MFM_MAXDRIVES) { number_mfm_drives = MFM_MAXDRIVES; printk("No. of ADFS MFM drives is greater than MFM_MAXDRIVES - you can't have that many!\n"); } for (drive = 0; drive < number_mfm_drives; drive++) { mfm_info[drive].lowcurrent = 1; mfm_info[drive].precomp = 1; mfm_info[drive].cylinder = -1; mfm_info[drive].errors.recal = 0; mfm_info[drive].errors.report = 0; mfm_info[drive].errors.abort = 4;#ifdef CONFIG_BLK_DEV_MFM_AUTODETECT mfm_info[drive].cylinders = 1024; mfm_info[drive].heads = 8; mfm_info[drive].sectors = 64; { unsigned char cmdb[16]; mfm_setupspecify (drive, cmdb); cmdb[1] &= ~0x81; issue_command (CMD_SPC, cmdb, 16); wait_for_completion (); if (!mfm_detectdrive (drive)) { mfm_info[drive].cylinders = 0; mfm_info[drive].heads = 0; mfm_info[drive].sectors = 0; } cmdb[0] = cmdb[1] = 0; issue_command (CMD_CKV, cmdb, 2); }#else mfm_info[drive].cylinders = 1; /* its going to have to figure it out from the partition info */ mfm_info[drive].heads = 4; mfm_info[drive].sectors = 32;#endif } return number_mfm_drives;}/* * The 'front' end of the mfm driver follows... */static int mfm_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg){ struct mfm_info *p = inode->i_bdev->bd_disk->private_data; struct hd_geometry *geo = (struct hd_geometry *) arg; if (cmd != HDIO_GETGEO) return -EINVAL; if (!arg) return -EINVAL; if (put_user (p->heads, &geo->heads)) return -EFAULT; if (put_user (p->sectors, &geo->sectors)) return -EFAULT; if (put_user (p->cylinders, &geo->cylinders)) return -EFAULT; if (put_user (get_start_sect(inode->i_bdev), &geo->start)) return -EFAULT; return 0;}/* * This is to handle various kernel command line parameters * specific to this driver. */void mfm_setup(char *str, int *ints){ return;}/* * Set the CHS from the ADFS boot block if it is present. This is not ideal * since if there are any non-ADFS partitions on the disk, this won't work! * Hence, I want to get rid of this... */void xd_set_geometry(struct block_device *bdev, unsigned char secsptrack, unsigned char heads, unsigned int secsize){ struct mfm_info *p = bdev->bd_disk->private_data; int drive = p - mfm_info; unsigned long disksize = bdev->bd_inode->i_size; if (p->cylinders == 1) { p->sectors = secsptrack; p->heads = heads; p->cylinders = discsize / (secsptrack * heads * secsize); if ((heads < 1) || (p->cylinders > 1024)) { printk("%s: Insane disc shape! Setting to 512/4/32\n", bdev->bd_disk->disk_name); /* These values are fairly arbitary, but are there so that if your * lucky you can pick apart your disc to find out what is going on - * I reckon these figures won't hurt MOST drives */ p->sectors = 32; p->heads = 4; p->cylinders = 512; } if (raw_cmd.dev == drive) mfm_specify (); mfm_geometry (drive); }}static struct block_device_operations mfm_fops ={ .owner = THIS_MODULE, .ioctl = mfm_ioctl,};/* * See if there is a controller at the address presently at mfm_addr * * We check to see if the controller is busy - if it is, we abort it first, * and check that the chip is no longer busy after at least 180 clock cycles. * We then issue a command and check that the BSY or CPR bits are set. */static int mfm_probecontroller (unsigned int mfm_addr){ if (inw (MFM_STATUS) & STAT_BSY) { outw (CMD_ABT, MFM_COMMAND); udelay (50); if (inw (MFM_STATUS) & STAT_BSY) return 0; } if (inw (MFM_STATUS) & STAT_CED) outw (CMD_RCAL, MFM_COMMAND); outw (CMD_SEK, MFM_COMMAND); if (inw (MFM_STATUS) & (STAT_BSY | STAT_CPR)) { unsigned int count = 2000; while (inw (MFM_STATUS) & STAT_BSY) { udelay (500); if (!--count) return 0; } outw (CMD_RCAL, MFM_COMMAND); } return 1;}static int mfm_do_init(unsigned char irqmask){ int i, ret; printk("mfm: found at address %08X, interrupt %d\n", mfm_addr, mfm_irq); ret = -EBUSY; if (!request_region (mfm_addr, 10, "mfm")) goto out1; ret = register_blkdev(MAJOR_NR, "mfm"); if (ret) goto out2; /* Stuff for the assembler routines to get to */ hdc63463_baseaddress = ioaddr(mfm_addr); hdc63463_irqpolladdress = mfm_IRQPollLoc; hdc63463_irqpollmask = irqmask; mfm_queue = blk_init_queue(do_mfm_request, &mfm_lock); if (!mfm_queue) goto out2a; Busy = 0; lastspecifieddrive = -1; mfm_drives = mfm_initdrives(); if (!mfm_drives) { ret = -ENODEV; goto out3; } for (i = 0; i < mfm_drives; i++) { struct gendisk *disk = alloc_disk(64); if (!disk) goto Enomem; disk->major = MAJOR_NR; disk->first_minor = i << 6; disk->fops = &mfm_fops; sprintf(disk->disk_name, "mfm%c", 'a'+i); mfm_gendisk[i] = disk; } printk("mfm: detected %d hard drive%s\n", mfm_drives, mfm_drives == 1 ? "" : "s"); ret = request_irq(mfm_irq, mfm_interrupt_handler, SA_INTERRUPT, "MFM harddisk", NULL); if (ret) { printk("mfm: unable to get IRQ%d\n", mfm_irq); goto out4; } if (mfm_irqenable) outw(0x80, mfm_irqenable); /* Required to enable IRQs from MFM podule */ for (i = 0; i < mfm_drives; i++) { mfm_geometry(i); mfm_gendisk[i]->queue = mfm_queue; add_disk(mfm_gendisk[i]); } return 0;out4: for (i = 0; i < mfm_drives; i++) put_disk(mfm_gendisk[i]);out3: blk_cleanup_queue(mfm_queue);out2a: unregister_blkdev(MAJOR_NR, "mfm");out2: release_region(mfm_addr, 10);out1: return ret;Enomem: while (i--) put_disk(mfm_gendisk[i]); goto out3;}static void mfm_do_exit(void){ int i; free_irq(mfm_irq, NULL); for (i = 0; i < mfm_drives; i++) { del_gendisk(mfm_gendisk[i]); put_disk(mfm_gendisk[i]); } blk_cleanup_queue(mfm_queue); unregister_blkdev(MAJOR_NR, "mfm"); if (mfm_addr) release_region(mfm_addr, 10);}static int __devinit mfm_probe(struct expansion_card *ec, struct ecard_id *id){ if (mfm_addr) return -EBUSY; mfm_addr = ecard_address(ec, ECARD_IOC, ECARD_MEDIUM) + 0x800; mfm_IRQPollLoc = ioaddr(mfm_addr + 0x400); mfm_irqenable = mfm_IRQPollLoc; mfm_irq = ec->irq; return mfm_do_init(0x08);}static void __devexit mfm_remove(struct expansion_card *ec){ outw (0, mfm_irqenable); /* Required to enable IRQs from MFM podule */ mfm_do_exit();}static const struct ecard_id mfm_cids[] = { { MANU_ACORN, PROD_ACORN_MFM }, { 0xffff, 0xffff },};static struct ecard_driver mfm_driver = { .probe = mfm_probe, .remove = __devexit(mfm_remove), .id_table = mfm_cids, .drv = { .name = "mfm", },};/* * Look for a MFM controller - first check the motherboard, then the podules * The podules have an extra interrupt enable that needs to be played with * * The HDC is accessed at MEDIUM IOC speeds. */static int __init mfm_init (void){ unsigned char irqmask; if (mfm_probecontroller(ONBOARD_MFM_ADDRESS)) { mfm_addr = ONBOARD_MFM_ADDRESS; mfm_IRQPollLoc = IOC_IRQSTATB; mfm_irqenable = 0; mfm_irq = IRQ_HARDDISK; return mfm_do_init(0x08); /* IL3 pin */ } else { return ecard_register_driver(&mfm_driver); }}static void __exit mfm_exit(void){ if (mfm_addr == ONBOARD_MFM_ADDRESS) mfm_do_exit(); else ecard_unregister_driver(&mfm_driver);}module_init(mfm_init)module_exit(mfm_exit)MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -