📄 mcd.c
字号:
/* This routine gets called during initialization if things go wrong, * and is used in mcd_exit as well. */static void cleanup(int level){ switch (level) { case 3: if (unregister_cdrom(&mcd_info)) { printk(KERN_WARNING "Can't unregister cdrom mcd\n"); return; } free_irq(mcd_irq, NULL); case 2: release_region(mcd_port, 4); case 1: if (devfs_unregister_blkdev(MAJOR_NR, "mcd")) { printk(KERN_WARNING "Can't unregister major mcd\n"); return; } blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); default:; }}/* * Test for presence of drive and initialize it. Called at boot time. */int __init mcd_init(void){ int count; unsigned char result[3]; char msg[80]; if (mcd_port <= 0 || mcd_irq <= 0) { printk(KERN_INFO "mcd: not probing.\n"); return -EIO; } if (devfs_register_blkdev(MAJOR_NR, "mcd", &mcd_bdops) != 0) { printk(KERN_ERR "mcd: Unable to get major %d for Mitsumi CD-ROM\n", MAJOR_NR); return -EIO; } if (check_region(mcd_port, 4)) { cleanup(1); printk(KERN_ERR "mcd: Initialization failed, I/O port (%X) already in use\n", mcd_port); return -EIO; } blksize_size[MAJOR_NR] = mcd_blocksizes; blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); read_ahead[MAJOR_NR] = 4; /* check for card */ outb(0, MCDPORT(1)); /* send reset */ for (count = 0; count < 2000000; count++) (void) inb(MCDPORT(1)); /* delay a bit */ outb(0x40, MCDPORT(0)); /* send get-stat cmd */ for (count = 0; count < 2000000; count++) if (!(inb(MCDPORT(1)) & MFL_STATUS)) break; if (count >= 2000000) { printk(KERN_INFO "mcd: initialisation failed - No mcd device at 0x%x irq %d\n", mcd_port, mcd_irq); cleanup(1); return -EIO; } count = inb(MCDPORT(0)); /* pick up the status */ outb(MCMD_GET_VERSION, MCDPORT(0)); for (count = 0; count < 3; count++) if (getValue(result + count)) { printk(KERN_ERR "mcd: mitsumi get version failed at 0x%x\n", mcd_port); cleanup(1); return -EIO; } if (result[0] == result[1] && result[1] == result[2]) { cleanup(1); return -EIO; } mcdVersion = result[2]; if (mcdVersion >= 4) outb(4, MCDPORT(2)); /* magic happens */ /* don't get the IRQ until we know for sure the drive is there */ if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD", NULL)) { printk(KERN_ERR "mcd: Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq); cleanup(1); return -EIO; } if (result[1] == 'D') { MCMD_DATA_READ = MCMD_2X_READ; /* Added flag to drop to 1x speed if too many errors */ mcdDouble = 1; } else mcd_info.speed = 1; sprintf(msg, " mcd: Mitsumi %s Speed CD-ROM at port=0x%x," " irq=%d\n", mcd_info.speed == 1 ? "Single" : "Double", mcd_port, mcd_irq); request_region(mcd_port, 4, "mcd"); outb(MCMD_CONFIG_DRIVE, MCDPORT(0)); outb(0x02, MCDPORT(0)); outb(0x00, MCDPORT(0)); getValue(result); outb(MCMD_CONFIG_DRIVE, MCDPORT(0)); outb(0x10, MCDPORT(0)); outb(0x04, MCDPORT(0)); getValue(result); mcd_invalidate_buffers(); mcdPresent = 1; mcd_info.dev = MKDEV(MAJOR_NR, 0); if (register_cdrom(&mcd_info) != 0) { printk(KERN_ERR "mcd: Unable to register Mitsumi CD-ROM.\n"); cleanup(3); return -EIO; } devfs_plain_cdrom(&mcd_info, &mcd_bdops); printk(msg); return 0;}static void hsg2msf(long hsg, struct msf *msf){ hsg += 150; msf->min = hsg / 4500; hsg %= 4500; msf->sec = hsg / 75; msf->frame = hsg % 75; bin2bcd(&msf->min); /* convert to BCD */ bin2bcd(&msf->sec); bin2bcd(&msf->frame);}static void bin2bcd(unsigned char *p){ int u, t; u = *p % 10; t = *p / 10; *p = u | (t << 4);}static int bcd2bin(unsigned char bcd){ return (bcd >> 4) * 10 + (bcd & 0xF);}/* * See if a status is ready from the drive and return it * if it is ready. */static int mcdStatus(void){ int i; int st; st = inb(MCDPORT(1)) & MFL_STATUS; if (!st) { i = inb(MCDPORT(0)) & 0xFF; return i; } else return -1;}/* * Send a play or read command to the drive */static void sendMcdCmd(int cmd, struct mcd_Play_msf *params){ outb(cmd, MCDPORT(0)); outb(params->start.min, MCDPORT(0)); outb(params->start.sec, MCDPORT(0)); outb(params->start.frame, MCDPORT(0)); outb(params->end.min, MCDPORT(0)); outb(params->end.sec, MCDPORT(0)); outb(params->end.frame, MCDPORT(0));}/* * Timer interrupt routine to test for status ready from the drive. * (see the next routine) */static void mcdStatTimer(unsigned long dummy){ if (!(inb(MCDPORT(1)) & MFL_STATUS)) { wake_up(&mcd_waitq); return; } McdTimeout--; if (McdTimeout <= 0) { wake_up(&mcd_waitq); return; } mcd_timer.function = mcdStatTimer; mod_timer(&mcd_timer, jiffies + 1);}/* * Wait for a status to be returned from the drive. The actual test * (see routine above) is done by the timer interrupt to avoid * excessive rescheduling. */static int getMcdStatus(int timeout){ int st; McdTimeout = timeout; mcd_timer.function = mcdStatTimer; mod_timer(&mcd_timer, jiffies + 1); sleep_on(&mcd_waitq); if (McdTimeout <= 0) return -1; st = inb(MCDPORT(0)) & 0xFF; if (st == 0xFF) return -1; if ((st & MST_BUSY) == 0 && audioStatus == CDROM_AUDIO_PLAY) /* XXX might be an error? look at q-channel? */ audioStatus = CDROM_AUDIO_COMPLETED; if (st & MST_DSK_CHG) { mcdDiskChanged = 1; tocUpToDate = 0; audioStatus = CDROM_AUDIO_NO_STATUS; } return st;}/* gives current state of the drive This function is quite unreliable, and should probably be rewritten by someone, eventually... */int mcd_drive_status(struct cdrom_device_info *cdi, int slot_nr){ int st; st = statusCmd(); /* check drive status */ if (st == -1) return -EIO; /* drive doesn't respond */ if ((st & MST_READY)) return CDS_DISC_OK; if ((st & MST_DOOR_OPEN)) return CDS_TRAY_OPEN; if ((st & MST_DSK_CHG)) return CDS_NO_DISC; if ((st & MST_BUSY)) return CDS_DRIVE_NOT_READY; return -EIO;}/* * Read a value from the drive. */static int getValue(unsigned char *result){ int count; int s; for (count = 0; count < 2000; count++) if (!(inb(MCDPORT(1)) & MFL_STATUS)) break; if (count >= 2000) { printk("mcd: getValue timeout\n"); return -1; } s = inb(MCDPORT(0)) & 0xFF; *result = (unsigned char) s; return 0;}/* * Read the current Q-channel info. Also used for reading the * table of contents. */int GetQChannelInfo(struct mcd_Toc *qp){ unsigned char notUsed; int retry; for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) { outb(MCMD_GET_Q_CHANNEL, MCDPORT(0)); if (getMcdStatus(MCD_STATUS_DELAY) != -1) break; } if (retry >= MCD_RETRY_ATTEMPTS) return -1; if (getValue(&qp->ctrl_addr) < 0) return -1; if (getValue(&qp->track) < 0) return -1; if (getValue(&qp->pointIndex) < 0) return -1; if (getValue(&qp->trackTime.min) < 0) return -1; if (getValue(&qp->trackTime.sec) < 0) return -1; if (getValue(&qp->trackTime.frame) < 0) return -1; if (getValue(¬Used) < 0) return -1; if (getValue(&qp->diskTime.min) < 0) return -1; if (getValue(&qp->diskTime.sec) < 0) return -1; if (getValue(&qp->diskTime.frame) < 0) return -1; return 0;}/* * Read the table of contents (TOC) and TOC header if necessary */static int updateToc(void){ if (tocUpToDate) return 0; if (GetDiskInfo() < 0) return -EIO; if (GetToc() < 0) return -EIO; tocUpToDate = 1; return 0;}/* * Read the table of contents header */static int GetDiskInfo(void){ int retry; for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) { outb(MCMD_GET_DISK_INFO, MCDPORT(0)); if (getMcdStatus(MCD_STATUS_DELAY) != -1) break; } if (retry >= MCD_RETRY_ATTEMPTS) return -1; if (getValue(&DiskInfo.first) < 0) return -1; if (getValue(&DiskInfo.last) < 0) return -1; DiskInfo.first = bcd2bin(DiskInfo.first); DiskInfo.last = bcd2bin(DiskInfo.last);#ifdef MCD_DEBUG printk ("Disk Info: first %d last %d length %02x:%02x.%02x first %02x:%02x.%02x\n", DiskInfo.first, DiskInfo.last, DiskInfo.diskLength.min, DiskInfo.diskLength.sec, DiskInfo.diskLength.frame, DiskInfo.firstTrack.min, DiskInfo.firstTrack.sec, DiskInfo.firstTrack.frame);#endif if (getValue(&DiskInfo.diskLength.min) < 0) return -1; if (getValue(&DiskInfo.diskLength.sec) < 0) return -1; if (getValue(&DiskInfo.diskLength.frame) < 0) return -1; if (getValue(&DiskInfo.firstTrack.min) < 0) return -1; if (getValue(&DiskInfo.firstTrack.sec) < 0) return -1; if (getValue(&DiskInfo.firstTrack.frame) < 0) return -1; return 0;}/* * Read the table of contents (TOC) */static int GetToc(void){ int i, px; int limit; int retry; struct mcd_Toc qInfo; for (i = 0; i < MAX_TRACKS; i++) Toc[i].pointIndex = 0; i = DiskInfo.last + 3; for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) { outb(MCMD_STOP, MCDPORT(0)); if (getMcdStatus(MCD_STATUS_DELAY) != -1) break; } if (retry >= MCD_RETRY_ATTEMPTS) return -1; for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) { outb(MCMD_SET_MODE, MCDPORT(0)); outb(0x05, MCDPORT(0)); /* mode: toc */ mcd_mode = 0x05; if (getMcdStatus(MCD_STATUS_DELAY) != -1) break; } if (retry >= MCD_RETRY_ATTEMPTS) return -1; for (limit = 300; limit > 0; limit--) { if (GetQChannelInfo(&qInfo) < 0) break; px = bcd2bin(qInfo.pointIndex); if (px > 0 && px < MAX_TRACKS && qInfo.track == 0) if (Toc[px].pointIndex == 0) { Toc[px] = qInfo; i--; } if (i <= 0) break; } Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength; for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) { outb(MCMD_SET_MODE, MCDPORT(0)); outb(0x01, MCDPORT(0)); mcd_mode = 1; if (getMcdStatus(MCD_STATUS_DELAY) != -1) break; }#ifdef MCD_DEBUG for (i = 1; i <= DiskInfo.last; i++) printk ("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n", i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame, Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame); for (i = 100; i < 103; i++) printk ("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n", i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame, Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);#endif return limit > 0 ? 0 : -1;}void __exit mcd_exit(void){ cleanup(3); del_timer_sync(&mcd_timer);}#ifdef MODULEmodule_init(mcd_init);#endifmodule_exit(mcd_exit);MODULE_AUTHOR("Martin Harriss");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -