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

📄 mcdx.c

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

		/* audio status? */
		if (stuffp->audiostatus == CDROM_AUDIO_INVALID)
			stuffp->audiostatus =
			    e_audiobusy(st) ? CDROM_AUDIO_PLAY :
			    CDROM_AUDIO_NO_STATUS;
		else if (stuffp->audiostatus == CDROM_AUDIO_PLAY
			 && e_audiobusy(st) == 0)
			stuffp->audiostatus = CDROM_AUDIO_COMPLETED;

		/* media change? */
		if (e_changed(st)) {
			xinfo("talk() media changed\n");
			stuffp->xxx = stuffp->yyy = 1;
		}

		/* now actually get the data */
		while (sz--) {
			if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) {
				xinfo("talk() %02x timed out (data), %d tr%s left\n",
				     cmd[0], tries - 1,
				     tries == 2 ? "y" : "ies");
				st = -1;
				break;
			}
			if (!discard)
				bp++;
			xtrace(TALK, "talk() got 0x%02x\n", *(bp - 1));
		}
	}

#if !MCDX_QUIET
	if (!tries && st == -1)
		xinfo("talk() giving up\n");
#endif

	stuffp->lock = 0;
	wake_up_interruptible(&stuffp->lockq);

	xtrace(TALK, "talk() done with 0x%02x\n", st);
	return st;
}

/* MODULE STUFF ***********************************************************/

EXPORT_NO_SYMBOLS;

int __mcdx_init(void)
{
	int i;
	int drives = 0;

	mcdx_init();
	for (i = 0; i < MCDX_NDRIVES; i++) {
		if (mcdx_stuffp[i]) {
			xtrace(INIT, "init_module() drive %d stuff @ %p\n",
			       i, mcdx_stuffp[i]);
			drives++;
		}
	}

	if (!drives)
		return -EIO;

	return 0;
}

void __exit mcdx_exit(void)
{
	int i;

	xinfo("cleanup_module called\n");

	for (i = 0; i < MCDX_NDRIVES; i++) {
		struct s_drive_stuff *stuffp;
		if (unregister_cdrom(&mcdx_info)) {
			printk(KERN_WARNING "Can't unregister cdrom mcdx\n");
			return;
		}
		stuffp = mcdx_stuffp[i];
		if (!stuffp)
			continue;
		release_region((unsigned long) stuffp->wreg_data,
			       MCDX_IO_SIZE);
		free_irq(stuffp->irq, NULL);
		if (stuffp->toc) {
			xtrace(MALLOC, "cleanup_module() free toc @ %p\n",
			       stuffp->toc);
			kfree(stuffp->toc);
		}
		xtrace(MALLOC, "cleanup_module() free stuffp @ %p\n",
		       stuffp);
		mcdx_stuffp[i] = NULL;
		kfree(stuffp);
	}

	if (devfs_unregister_blkdev(MAJOR_NR, "mcdx") != 0) {
		xwarn("cleanup() unregister_blkdev() failed\n");
	}
	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
#if !MCDX_QUIET
	else
	xinfo("cleanup() succeeded\n");
#endif
}

#ifdef MODULE
module_init(__mcdx_init);
#endif
module_exit(mcdx_exit);


/* Support functions ************************************************/

int __init mcdx_init_drive(int drive)
{
	struct s_version version;
	struct s_drive_stuff *stuffp;
	int size = sizeof(*stuffp);
	char msg[80];

	mcdx_blocksizes[drive] = 0;

	xtrace(INIT, "init() try drive %d\n", drive);

	xtrace(INIT, "kmalloc space for stuffpt's\n");
	xtrace(MALLOC, "init() malloc %d bytes\n", size);
	if (!(stuffp = kmalloc(size, GFP_KERNEL))) {
		xwarn("init() malloc failed\n");
		return 1;
	}

	xtrace(INIT, "init() got %d bytes for drive stuff @ %p\n",
	       sizeof(*stuffp), stuffp);

	/* set default values */
	memset(stuffp, 0, sizeof(*stuffp));

	stuffp->present = 0;	/* this should be 0 already */
	stuffp->toc = NULL;	/* this should be NULL already */

	/* setup our irq and i/o addresses */
	stuffp->irq = irq(mcdx_drive_map[drive]);
	stuffp->wreg_data = stuffp->rreg_data =
	    port(mcdx_drive_map[drive]);
	stuffp->wreg_reset = stuffp->rreg_status = stuffp->wreg_data + 1;
	stuffp->wreg_hcon = stuffp->wreg_reset + 1;
	stuffp->wreg_chn = stuffp->wreg_hcon + 1;

	init_waitqueue_head(&stuffp->busyq);
	init_waitqueue_head(&stuffp->lockq);
	init_waitqueue_head(&stuffp->sleepq);

	/* check if i/o addresses are available */
	if (check_region((unsigned int) stuffp->wreg_data, MCDX_IO_SIZE)) {
		xwarn("0x%3p,%d: Init failed. "
		      "I/O ports (0x%3p..0x%3p) already in use.\n",
		      stuffp->wreg_data, stuffp->irq,
		      stuffp->wreg_data,
		      stuffp->wreg_data + MCDX_IO_SIZE - 1);
		xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp);
		kfree(stuffp);
		xtrace(INIT, "init() continue at next drive\n");
		return 0;	/* next drive */
	}

	xtrace(INIT, "init() i/o port is available at 0x%3p\n",
	       stuffp->wreg_data);
	xtrace(INIT, "init() hardware reset\n");
	mcdx_reset(stuffp, HARD, 1);

	xtrace(INIT, "init() get version\n");
	if (-1 == mcdx_requestversion(stuffp, &version, 4)) {
		/* failed, next drive */
		xwarn("%s=0x%3p,%d: Init failed. Can't get version.\n",
		      MCDX, stuffp->wreg_data, stuffp->irq);
		xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp);
		kfree(stuffp);
		xtrace(INIT, "init() continue at next drive\n");
		return 0;
	}

	switch (version.code) {
	case 'D':
		stuffp->readcmd = READ2X;
		stuffp->present = DOUBLE | DOOR | MULTI;
		break;
	case 'F':
		stuffp->readcmd = READ1X;
		stuffp->present = SINGLE | DOOR | MULTI;
		break;
	case 'M':
		stuffp->readcmd = READ1X;
		stuffp->present = SINGLE;
		break;
	default:
		stuffp->present = 0;
		break;
	}

	stuffp->playcmd = READ1X;

	if (!stuffp->present) {
		xwarn("%s=0x%3p,%d: Init failed. No Mitsumi CD-ROM?.\n",
		      MCDX, stuffp->wreg_data, stuffp->irq);
		kfree(stuffp);
		return 0;	/* next drive */
	}

	xtrace(INIT, "init() register blkdev\n");
	if (devfs_register_blkdev(MAJOR_NR, "mcdx", &mcdx_bdops) != 0) {
		xwarn("%s=0x%3p,%d: Init failed. Can't get major %d.\n",
		      MCDX, stuffp->wreg_data, stuffp->irq, MAJOR_NR);
		kfree(stuffp);
		return 1;
	}

	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
	read_ahead[MAJOR_NR] = READ_AHEAD;
	blksize_size[MAJOR_NR] = mcdx_blocksizes;

	xtrace(INIT, "init() subscribe irq and i/o\n");
	mcdx_irq_map[stuffp->irq] = stuffp;
	if (request_irq(stuffp->irq, mcdx_intr, SA_INTERRUPT, "mcdx", NULL)) {
		xwarn("%s=0x%3p,%d: Init failed. Can't get irq (%d).\n",
		      MCDX, stuffp->wreg_data, stuffp->irq, stuffp->irq);
		stuffp->irq = 0;
		blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
		kfree(stuffp);
		return 0;
	}
	request_region((unsigned int) stuffp->wreg_data,
		       MCDX_IO_SIZE, "mcdx");

	xtrace(INIT, "init() get garbage\n");
	{
		int i;
		mcdx_delay(stuffp, HZ / 2);
		for (i = 100; i; i--)
			(void) inb((unsigned int) stuffp->rreg_status);
	}


#if WE_KNOW_WHY
	/* irq 11 -> channel register */
	outb(0x50, (unsigned int) stuffp->wreg_chn);
#endif

	xtrace(INIT, "init() set non dma but irq mode\n");
	mcdx_config(stuffp, 1);

	stuffp->minor = drive;

	sprintf(msg, " mcdx: Mitsumi CD-ROM installed at 0x%3p, irq %d."
		" (Firmware version %c %x)\n",
		stuffp->wreg_data, stuffp->irq, version.code, version.ver);
	mcdx_stuffp[drive] = stuffp;
	xtrace(INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp);
	mcdx_info.dev = MKDEV(MAJOR_NR, 0);
	if (register_cdrom(&mcdx_info) != 0) {
		printk("Cannot register Mitsumi CD-ROM!\n");
		release_region((unsigned long) stuffp->wreg_data,
			       MCDX_IO_SIZE);
		free_irq(stuffp->irq, NULL);
		kfree(stuffp);
		if (devfs_unregister_blkdev(MAJOR_NR, "mcdx") != 0)
			xwarn("cleanup() unregister_blkdev() failed\n");
		blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
		return 2;
	}
	devfs_plain_cdrom(&mcdx_info, &mcdx_bdops);
	printk(msg);
	return 0;
}

int __init mcdx_init(void)
{
	int drive;
#ifdef MODULE
	xwarn("Version 2.14(hs) for " UTS_RELEASE "\n");
#else
	xwarn("Version 2.14(hs) \n");
#endif

	xwarn("$Id: mcdx.c,v 1.21 1997/01/26 07:12:59 davem Exp $\n");

	/* zero the pointer array */
	for (drive = 0; drive < MCDX_NDRIVES; drive++)
		mcdx_stuffp[drive] = NULL;

	/* do the initialisation */
	for (drive = 0; drive < MCDX_NDRIVES; drive++) {
		switch (mcdx_init_drive(drive)) {
		case 2:
			return -EIO;
		case 1:
			break;
		}
	}
	return 0;
}

static int mcdx_transfer(struct s_drive_stuff *stuffp,
	      char *p, int sector, int nr_sectors)
/*	This seems to do the actually transfer.  But it does more.  It
	keeps track of errors occurred and will (if possible) fall back
	to single speed on error.
	Return:	-1 on timeout or other error
			else status byte (as in stuff->st) */
{
	int ans;

	ans = mcdx_xfer(stuffp, p, sector, nr_sectors);
	return ans;
#if FALLBACK
	if (-1 == ans)
		stuffp->readerrs++;
	else
		return ans;

	if (stuffp->readerrs && stuffp->readcmd == READ1X) {
		xwarn("XXX Already reading 1x -- no chance\n");
		return -1;
	}

	xwarn("XXX Fallback to 1x\n");

	stuffp->readcmd = READ1X;
	return mcdx_transfer(stuffp, p, sector, nr_sectors);
#endif

}


static int mcdx_xfer(struct s_drive_stuff *stuffp,
		     char *p, int sector, int nr_sectors)
/*	This does actually the transfer from the drive.
	Return:	-1 on timeout or other error
			else status byte (as in stuff->st) */
{
	int border;
	int done = 0;
	long timeout;

	if (stuffp->audio) {
		xwarn("Attempt to read from audio CD.\n");
		return -1;
	}

	if (!stuffp->readcmd) {
		xinfo("Can't transfer from missing disk.\n");
		return -1;
	}

	while (stuffp->lock) {
		interruptible_sleep_on(&stuffp->lockq);
	}

	if (stuffp->valid && (sector >= stuffp->pending)
	    && (sector < stuffp->low_border)) {

		/* All (or at least a part of the sectors requested) seems
		   * to be already requested, so we don't need to bother the
		   * drive with new requests ...
		   * Wait for the drive become idle, but first
		   * check for possible occurred errors --- the drive
		   * seems to report them asynchronously */


		border = stuffp->high_border < (border =
						sector + nr_sectors)
		    ? stuffp->high_border : border;

		stuffp->lock = current->pid;

		do {

			while (stuffp->busy) {

				timeout =
				    interruptible_sleep_on_timeout
				    (&stuffp->busyq, 5 * HZ);

				if (!stuffp->introk) {
					xtrace(XFER,
					       "error via interrupt\n");
				} else if (!timeout) {
					xtrace(XFER, "timeout\n");
				} else if (signal_pending(current)) {
					xtrace(XFER, "signal\n");
				} else
					continue;

				stuffp->lock = 0;
				stuffp->busy = 0;
				stuffp->valid = 0;

				wake_up_interruptible(&stuffp->lockq);
				xtrace(XFER, "transfer() done (-1)\n");
				return -1;
			}

			/* check if we need to set the busy flag (as we
			 * expect an interrupt */
			stuffp->busy = (3 == (stuffp->pending & 3));

			/* Test if it's the first sector of a block,
			 * there we have to skip some bytes as we read raw data */
			if (stuffp->xa && (0 == (stuffp->pending & 3))) {
				const int HEAD =
				    CD_FRAMESIZE_RAW - CD_XA_TAIL -
				    CD_FRAMESIZE;
				insb((unsigned int) stuffp->rreg_data, p,
				     HEAD);
			}

			/* now actually read the data */
			insb((unsigned int) stuffp->rreg_data, p, 512);

			/* test if it's the last sector of a block,
			 * if so, we have to handle XA special */
			if ((3 == (stuffp->pending & 3)) && stuffp->xa) {
				char dummy[CD_XA_TAIL];
				insb((unsigned int) stuffp->rreg_data,
				     &dummy[0], CD_XA_TAIL);
			}

			if (stuffp->pending == sector) {
				p += 512;
				done++;
				sector++;
			}
		} while (++(stuffp->pending) < border);

		stuffp->lock = 0;
		wake_up_interruptible(&stuffp->lockq);

	} else {

		/* The requested sector(s) is/are out of the
		 * already requested range, so we have to bother the drive
		 * with a new request. */

		static unsigned char cmd[] = {
			0,
			0, 0, 0,
			0, 0, 0
		};

		cmd[0] = stuffp->readcmd;

		/* The numbers held in ->pending, ..., should be valid */
		stuffp->valid = 1;
		stuffp->pending = sector & ~3;

		/* do some sanity checks */
		if (stuffp->pending > stuffp->lastsector) {
			xwarn
			    ("transfer() sector %d from nirvana requested.\n",
			     stuffp->pending);
			stuffp->status = MCDX_ST_EOM;
			stuffp->valid = 0;
			xtrace(XFER, "transfer() done (-1)\n");
			return -1;
		}

		if ((stuffp->low_border = stuffp->pending + DIRECT_SIZE)
		    > stuffp->lastsector + 1) {
			xtrace(XFER, "cut low_border\n");
			stuffp->low_border = stuffp->lastsector + 1;
		}
		if ((stuffp->high_border = stuffp->pending + REQUEST_SIZE)
		    > stuffp->lastsector + 1) {
			xtrace(XFER, "cut high_border\n");
			stuffp->high_border = stuffp->lastsector + 1;
		}

		{		/* Convert the sector to be requested to MSF format */
			struct cdrom_msf0 pending;
			log2msf(stuffp->pending / 4, &pending);
			cmd[1] = pending.minute;
			cmd[2] = pending.second;
			cmd[3] = pending.frame;
		}

		cmd[6] =

⌨️ 快捷键说明

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