⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mcd.c

📁 cdrom device drive for linux.
💻 C
📖 第 1 页 / 共 3 页
字号:

/* 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(&notUsed) < 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 MODULE
module_init(mcd_init);
#endif
module_exit(mcd_exit);

MODULE_AUTHOR("Martin Harriss");
MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -