📄 mfmhd.c
字号:
/* * Tell the user about the drive if we decided it exists. */static void mfm_geometry (int drive){ if (mfm_info[drive].cylinders) printk ("mfm%c: %dMB CHS=%d/%d/%d LCC=%d RECOMP=%d\n", 'a' + drive, mfm_info[drive].cylinders * mfm_info[drive].heads * mfm_info[drive].sectors / 4096, mfm_info[drive].cylinders, mfm_info[drive].heads, mfm_info[drive].sectors, mfm_info[drive].lowcurrent, mfm_info[drive].precomp);}#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 hd_geometry *geo = (struct hd_geometry *) arg; kdev_t dev; int device, major, minor, err; if (!inode || !(dev = inode->i_rdev)) return -EINVAL; major = MAJOR(dev); minor = MINOR(dev); device = DEVICE_NR(MINOR(inode->i_rdev)), err; if (device >= mfm_drives) return -EINVAL; switch (cmd) { case HDIO_GETGEO: if (!arg) return -EINVAL; if (put_user (mfm_info[device].heads, &geo->heads)) return -EFAULT; if (put_user (mfm_info[device].sectors, &geo->sectors)) return -EFAULT; if (put_user (mfm_info[device].cylinders, &geo->cylinders)) return -EFAULT; if (put_user (mfm[minor].start_sect, &geo->start)) return -EFAULT; return 0; case BLKFRASET: if (!capable(CAP_SYS_ADMIN)) return -EACCES; max_readahead[major][minor] = arg; return 0; case BLKFRAGET: return put_user(max_readahead[major][minor], (long *) arg); case BLKSECTGET: return put_user(max_sectors[major][minor], (long *) arg); case BLKRRPART: if (!capable(CAP_SYS_ADMIN)) return -EACCES; return mfm_reread_partitions(dev); case BLKGETSIZE: case BLKGETSIZE64: case BLKFLSBUF: case BLKROSET: case BLKROGET: case BLKRASET: case BLKRAGET: case BLKPG: return blk_ioctl(dev, cmd, arg); default: return -EINVAL; }}static int mfm_open(struct inode *inode, struct file *file){ int dev = DEVICE_NR(MINOR(inode->i_rdev)); if (dev >= mfm_drives) return -ENODEV; while (mfm_info[dev].busy) sleep_on (&mfm_wait_open); mfm_info[dev].access_count++; return 0;}/* * Releasing a block device means we sync() it, so that it can safely * be forgotten about... */static int mfm_release(struct inode *inode, struct file *file){ mfm_info[DEVICE_NR(MINOR(inode->i_rdev))].access_count--; 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(kdev_t dev, unsigned char secsptrack, unsigned char heads, unsigned long discsize, unsigned int secsize){ int drive = MINOR(dev) >> 6; if (mfm_info[drive].cylinders == 1) { mfm_info[drive].sectors = secsptrack; mfm_info[drive].heads = heads; mfm_info[drive].cylinders = discsize / (secsptrack * heads * secsize); if ((heads < 1) || (mfm_info[drive].cylinders > 1024)) { printk("mfm%c: Insane disc shape! Setting to 512/4/32\n",'a' + (dev >> 6)); /* 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 */ mfm_info[drive].sectors = 32; mfm_info[drive].heads = 4; mfm_info[drive].cylinders = 512; } if (raw_cmd.dev == drive) mfm_specify (); mfm_geometry (drive); mfm[drive << 6].start_sect = 0; mfm[drive << 6].nr_sects = mfm_info[drive].cylinders * mfm_info[drive].heads * mfm_info[drive].sectors / 2; }}static struct gendisk mfm_gendisk = { major: MAJOR_NR, major_name: "mfm", minor_shift: 6, max_p: 1 << 6, part: mfm, sizes: mfm_sizes, real_devices: (void *)mfm_info,};static struct block_device_operations mfm_fops ={ owner: THIS_MODULE, open: mfm_open, release: mfm_release, ioctl: mfm_ioctl,};static void mfm_geninit (void){ int i; for (i = 0; i < (MFM_MAXDRIVES << 6); i++) { /* Can't increase this - if you do all hell breaks loose */ mfm_blocksizes[i] = 1024; mfm_sectsizes[i] = 512; } blksize_size[MAJOR_NR] = mfm_blocksizes; hardsect_size[MAJOR_NR] = mfm_sectsizes; mfm_drives = mfm_initdrives(); printk("mfm: detected %d hard drive%s\n", mfm_drives, mfm_drives == 1 ? "" : "s"); mfm_gendisk.nr_real = mfm_drives; if (request_irq(mfm_irq, mfm_interrupt_handler, SA_INTERRUPT, "MFM harddisk", NULL)) printk("mfm: unable to get IRQ%d\n", mfm_irq); if (mfm_irqenable) outw(0x80, mfm_irqenable); /* Required to enable IRQs from MFM podule */ for (i = 0; i < mfm_drives; i++) { mfm_geometry (i); register_disk(&mfm_gendisk, MKDEV(MAJOR_NR,i<<6), 1<<6, &mfm_fops, mfm_info[i].cylinders * mfm_info[i].heads * mfm_info[i].sectors / 2); }}static struct expansion_card *ecs;/* * 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;}/* * 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. */int 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; irqmask = 0x08; /* IL3 pin */ } else { ecs = ecard_find(0, mfm_cids); if (!ecs) { mfm_addr = 0; return -1; } mfm_addr = ecard_address(ecs, ECARD_IOC, ECARD_MEDIUM) + 0x800; mfm_IRQPollLoc = ioaddr(mfm_addr + 0x400); mfm_irqenable = mfm_IRQPollLoc; mfm_irq = ecs->irq; irqmask = 0x08; ecard_claim(ecs); } printk("mfm: found at address %08X, interrupt %d\n", mfm_addr, mfm_irq); if (!request_region (mfm_addr, 10, "mfm")) { ecard_release(ecs); return -1; } if (register_blkdev(MAJOR_NR, "mfm", &mfm_fops)) { printk("mfm_init: unable to get major number %d\n", MAJOR_NR); ecard_release(ecs); release_region(mfm_addr, 10); return -1; } /* Stuff for the assembler routines to get to */ hdc63463_baseaddress = ioaddr(mfm_addr); hdc63463_irqpolladdress = mfm_IRQPollLoc; hdc63463_irqpollmask = irqmask; blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB?) read ahread */ add_gendisk(&mfm_gendisk); Busy = 0; lastspecifieddrive = -1; mfm_geninit(); return 0;}/* * This routine is called to flush all partitions and partition tables * for a changed MFM disk, and then re-read the new partition table. * If we are revalidating due to an ioctl, we have USAGE == 1. */static int mfm_reread_partitions(kdev_t dev){ unsigned int start, i, maxp, target = DEVICE_NR(MINOR(dev)); unsigned long flags; save_flags_cli(flags); if (mfm_info[target].busy || mfm_info[target].access_count > 1) { restore_flags (flags); return -EBUSY; } mfm_info[target].busy = 1; restore_flags (flags); maxp = mfm_gendisk.max_p; start = target << mfm_gendisk.minor_shift; for (i = maxp - 1; i >= 0; i--) { int minor = start + i; invalidate_device (MKDEV(MAJOR_NR, minor), 1); mfm_gendisk.part[minor].start_sect = 0; mfm_gendisk.part[minor].nr_sects = 0; } /* Divide by 2, since sectors are 2 times smaller than usual ;-) */ grok_partitions(&mfm_gendisk, target, 1<<6, mfm_info[target].heads * mfm_info[target].cylinders * mfm_info[target].sectors / 2); mfm_info[target].busy = 0; wake_up (&mfm_wait_open); return 0;}#ifdef MODULEEXPORT_NO_SYMBOLS;MODULE_LICENSE("GPL");int init_module(void){ return mfm_init();}void cleanup_module(void){ if (ecs && mfm_irqenable) outw (0, mfm_irqenable); /* Required to enable IRQs from MFM podule */ free_irq(mfm_irq, NULL); unregister_blkdev(MAJOR_NR, "mfm"); del_gendisk(&mfm_gendisk); if (ecs) ecard_release(ecs); if (mfm_addr) release_region(mfm_addr, 10);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -