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

📄 mcd.c

📁 <Linux1.0核心游记>电子书+书后源码+Linux1.0源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/*	linux/kernel/blk_drv/mcd.c - Mitsumi CDROM driver	Copyright (C) 1992  Martin Harriss	martin@bdsi.com	This program is free software; you can redistribute it and/or modify	it under the terms of the GNU General Public License as published by	the Free Software Foundation; either version 2, or (at your option)	any later version.	This program is distributed in the hope that it will be useful,	but WITHOUT ANY WARRANTY; without even the implied warranty of	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the	GNU General Public License for more details.	You should have received a copy of the GNU General Public License	along with this program; if not, write to the Free Software	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.	HISTORY	0.1	First attempt - internal use only	0.2	Cleaned up delays and use of timer - alpha release	0.3	Audio support added	0.3.1 Changes for mitsumi CRMC LU005S march version		   (stud11@cc4.kuleuven.ac.be)        0.3.2 bug fixes to the ioclts and merged with ALPHA0.99-pl12		   (Jon Tombs <jon@robots.ox.ac.uk>)        0.3.3 Added more #defines and mcd_setup()   		   (Jon Tombs <jon@gtex02.us.es>)*/#include <linux/errno.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/fs.h>#include <linux/kernel.h>#include <linux/cdrom.h>#include <linux/ioport.h>/* #define REALLY_SLOW_IO  */#include <asm/system.h>#include <asm/io.h>#include <asm/segment.h>#define MAJOR_NR MITSUMI_CDROM_MAJOR#include "blk.h"#include <linux/mcd.h>#if 0static int mcd_sizes[] = { 0 };#endifstatic int mcdPresent = 0;static char mcd_buf[2048];	/* buffer for block size conversion */static int   mcd_bn   = -1;static short mcd_port = MCD_BASE_ADDR;static int   mcd_irq  = MCD_INTR_NR;static int McdTimeout, McdTries;static struct wait_queue *mcd_waitq = NULL;static struct mcd_DiskInfo DiskInfo;static struct mcd_Toc Toc[MAX_TRACKS];static struct mcd_Play_msf mcd_Play;static int audioStatus;static char mcdDiskChanged;static char tocUpToDate;static char mcdVersion;static void mcd_transfer(void);static void mcd_start(void);static void mcd_status(void);static void mcd_read_cmd(void);static void mcd_data(void);static void do_mcd_request(void);static void hsg2msf(long hsg, struct msf *msf);static void bin2bcd(unsigned char *p);static int bcd2bin(unsigned char bcd);static int mcdStatus(void);static void sendMcdCmd(int cmd, struct mcd_Play_msf *params);static int getMcdStatus(int timeout);static int GetQChannelInfo(struct mcd_Toc *qp);static int updateToc(void);static int GetDiskInfo(void);static int GetToc(void);static int getValue(unsigned char *result);void mcd_setup(char *str, int *ints){   if (ints[0] > 0)      mcd_port = ints[1];   if (ints[0] > 1)            mcd_irq  = ints[2];} intcheck_mcd_media_change(int full_dev, int flag){   int retval, target;#if 1	 /* the below is not reliable */   return 0;#endif     target = MINOR(full_dev);   if (target > 0) {      printk("mcd: Mitsumi CD-ROM request error: invalid device.\n");      return 0;   }   retval = mcdDiskChanged;   if (!flag)   {      mcdDiskChanged = 0;   }   return retval;}/* * Do a 'get status' command and get the result.  Only use from the top half * because it calls 'getMcdStatus' which sleeps. */static intstatusCmd(void){	int st, retry;	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)	{		outb(MCMD_GET_STATUS, MCDPORT(0));	/* send get-status cmd */		st = getMcdStatus(MCD_STATUS_DELAY);		if (st != -1)			break;	}	return st;}/* * Send a 'Play' command and get the status.  Use only from the top half. */static intmcdPlay(struct mcd_Play_msf *arg){	int retry, st;	for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)	{		sendMcdCmd(MCMD_PLAY_READ, arg);		st = getMcdStatus(2 * MCD_STATUS_DELAY);		if (st != -1)			break;	}	return st;}longmsf2hsg(struct msf *mp){	return bcd2bin(mp -> frame)		+ bcd2bin(mp -> sec) * 75		+ bcd2bin(mp -> min) * 4500		- 150;}static intmcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,						unsigned long arg){	int i, st;	struct mcd_Toc qInfo;	struct cdrom_ti ti;	struct cdrom_tochdr tocHdr;	struct cdrom_msf msf;	struct cdrom_tocentry entry;	struct mcd_Toc *tocPtr;	struct cdrom_subchnl subchnl;#if 0	struct cdrom_volctrl volctrl;#endif	if (!ip)		return -EINVAL;	st = statusCmd();	if (st < 0)		return -EIO;	if (!tocUpToDate)	{		i = updateToc();		if (i < 0)			return i;	/* error reading TOC */	}	switch (cmd)	{	case CDROMSTART:     /* Spin up the drive */		/* Don't think we can do this.  Even if we could, 		 * I think the drive times out and stops after a while		 * anyway.  For now, ignore it.		 */		return 0;	case CDROMSTOP:      /* Spin down the drive */		outb(MCMD_STOP, MCDPORT(0));		i = getMcdStatus(MCD_STATUS_DELAY);		/* should we do anything if it fails? */		audioStatus = CDROM_AUDIO_NO_STATUS;		return 0;	case CDROMPAUSE:     /* Pause the drive */		if (audioStatus != CDROM_AUDIO_PLAY)			return -EINVAL;		outb(MCMD_STOP, MCDPORT(0));		i = getMcdStatus(MCD_STATUS_DELAY);		if (GetQChannelInfo(&qInfo) < 0)		{			/* didn't get q channel info */			audioStatus = CDROM_AUDIO_NO_STATUS;			return 0;		}		mcd_Play.start = qInfo.diskTime;	/* remember restart point */		audioStatus = CDROM_AUDIO_PAUSED;		return 0;	case CDROMRESUME:    /* Play it again, Sam */		if (audioStatus != CDROM_AUDIO_PAUSED)			return -EINVAL;		/* restart the drive at the saved position. */		i = mcdPlay(&mcd_Play);		if (i < 0)		{			audioStatus = CDROM_AUDIO_ERROR;			return -EIO;		}		audioStatus = CDROM_AUDIO_PLAY;		return 0;	case CDROMPLAYTRKIND:     /* Play a track.  This currently ignores index. */		st = verify_area(VERIFY_READ, (void *) arg, sizeof ti);		if (st)			return st;		memcpy_fromfs(&ti, (void *) arg, sizeof ti);		if (ti.cdti_trk0 < DiskInfo.first			|| ti.cdti_trk0 > DiskInfo.last			|| ti.cdti_trk1 < ti.cdti_trk0)		{			return -EINVAL;		}		if (ti.cdti_trk1 > DiskInfo.last)			ti. cdti_trk1 = DiskInfo.last;		mcd_Play.start = Toc[ti.cdti_trk0].diskTime;		mcd_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;#ifdef MCD_DEBUGprintk("play: %02x:%02x.%02x to %02x:%02x.%02x\n",	mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame,	mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame);#endif		i = mcdPlay(&mcd_Play);		if (i < 0)		{			audioStatus = CDROM_AUDIO_ERROR;			return -EIO;		}		audioStatus = CDROM_AUDIO_PLAY;		return 0;	case CDROMPLAYMSF:   /* Play starting at the given MSF address. */		if (audioStatus == CDROM_AUDIO_PLAY) {		  outb(MCMD_STOP, MCDPORT(0));		  i = getMcdStatus(MCD_STATUS_DELAY);		  audioStatus = CDROM_AUDIO_NO_STATUS;		}		st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);		if (st)			return st;		memcpy_fromfs(&msf, (void *) arg, sizeof msf);		/* convert to bcd */		bin2bcd(&msf.cdmsf_min0);		bin2bcd(&msf.cdmsf_sec0);		bin2bcd(&msf.cdmsf_frame0);		bin2bcd(&msf.cdmsf_min1);		bin2bcd(&msf.cdmsf_sec1);		bin2bcd(&msf.cdmsf_frame1);		mcd_Play.start.min = msf.cdmsf_min0;		mcd_Play.start.sec = msf.cdmsf_sec0;		mcd_Play.start.frame = msf.cdmsf_frame0;		mcd_Play.end.min = msf.cdmsf_min1;		mcd_Play.end.sec = msf.cdmsf_sec1;		mcd_Play.end.frame = msf.cdmsf_frame1;#ifdef MCD_DEBUGprintk("play: %02x:%02x.%02x to %02x:%02x.%02x\n",mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame,mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame);#endif		i = mcdPlay(&mcd_Play);		if (i < 0)		{			audioStatus = CDROM_AUDIO_ERROR;			return -EIO;		}		audioStatus = CDROM_AUDIO_PLAY;		return 0;	case CDROMREADTOCHDR:        /* Read the table of contents header */		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr);		if (st)			return st;		tocHdr.cdth_trk0 = DiskInfo.first;		tocHdr.cdth_trk1 = DiskInfo.last;		memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);		return 0;	case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);		if (st)			return st;		memcpy_fromfs(&entry, (void *) arg, sizeof entry);		if (entry.cdte_track == CDROM_LEADOUT)			/* XXX */			tocPtr = &Toc[DiskInfo.last + 1];		else if (entry.cdte_track > DiskInfo.last				|| entry.cdte_track < DiskInfo.first)			return -EINVAL;		else			tocPtr = &Toc[entry.cdte_track];		entry.cdte_adr = tocPtr -> ctrl_addr;		entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;		if (entry.cdte_format == CDROM_LBA)			entry.cdte_addr.lba = msf2hsg(&tocPtr -> diskTime);		else if (entry.cdte_format == CDROM_MSF)		{			entry.cdte_addr.msf.minute = bcd2bin(tocPtr -> diskTime.min);			entry.cdte_addr.msf.second = bcd2bin(tocPtr -> diskTime.sec);			entry.cdte_addr.msf.frame = bcd2bin(tocPtr -> diskTime.frame);		}		else			return -EINVAL;		memcpy_tofs((void *) arg, &entry, sizeof entry);		return 0;	case CDROMSUBCHNL:   /* Get subchannel info */		st = verify_area(VERIFY_WRITE, (void *) arg, sizeof subchnl);		if (st)			return st;		memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl);		if (GetQChannelInfo(&qInfo) < 0)			return -EIO;		subchnl.cdsc_audiostatus = audioStatus;		subchnl.cdsc_adr = qInfo.ctrl_addr;		subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4;		subchnl.cdsc_trk = bcd2bin(qInfo.track);		subchnl.cdsc_ind = bcd2bin(qInfo.pointIndex);		if (subchnl.cdsc_format == CDROM_LBA)		{			subchnl.cdsc_absaddr.lba = msf2hsg(&qInfo.diskTime);			subchnl.cdsc_reladdr.lba = msf2hsg(&qInfo.trackTime);		}		else if (subchnl.cdsc_format == CDROM_MSF)		{			subchnl.cdsc_absaddr.msf.minute = bcd2bin(qInfo.diskTime.min);			subchnl.cdsc_absaddr.msf.second = bcd2bin(qInfo.diskTime.sec);			subchnl.cdsc_absaddr.msf.frame = bcd2bin(qInfo.diskTime.frame);			subchnl.cdsc_reladdr.msf.minute = bcd2bin(qInfo.trackTime.min);			subchnl.cdsc_reladdr.msf.second = bcd2bin(qInfo.trackTime.sec);			subchnl.cdsc_reladdr.msf.frame = bcd2bin(qInfo.trackTime.frame);		}		else			return -EINVAL;		memcpy_tofs((void *) arg, &subchnl, sizeof subchnl);		return 0;	case CDROMVOLCTRL:   /* Volume control */	/*	 * This is not working yet.  Setting the volume by itself does	 * nothing.  Following the 'set' by a 'play' results in zero	 * volume.  Something to work on for the next release.	 */#if 0		st = verify_area(VERIFY_READ, (void *) arg, sizeof(volctrl));		if (st)			return st;		memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));printk("VOL %d %d\n", volctrl.channel0 & 0xFF, volctrl.channel1 & 0xFF);		outb(MCMD_SET_VOLUME, MCDPORT(0));		outb(volctrl.channel0, MCDPORT(0));		outb(0, MCDPORT(0));		outb(volctrl.channel1, MCDPORT(0));		outb(1, MCDPORT(0));		i = getMcdStatus(MCD_STATUS_DELAY);		if (i < 0)			return -EIO;		{			int a, b, c, d;			getValue(&a);			getValue(&b);			getValue(&c);			getValue(&d);			printk("%02X %02X %02X %02X\n", a, b, c, d);		}		outb(0xF8, MCDPORT(0));		i = getMcdStatus(MCD_STATUS_DELAY);		printk("F8 -> %02X\n", i & 0xFF);#endif		return 0;	case CDROMEJECT:     /* Eject the drive - N/A */		return 0;	default:		return -EINVAL;	}}/* * Take care of the different block sizes between cdrom and Linux. * When Linux gets variable block sizes this will probably go away. */static voidmcd_transfer(void){	long offs;	while (CURRENT -> nr_sectors > 0 && mcd_bn == CURRENT -> sector / 4)	{		offs = (CURRENT -> sector & 3) * 512;		memcpy(CURRENT -> buffer, mcd_buf + offs, 512);		CURRENT -> nr_sectors--;		CURRENT -> sector++;		CURRENT -> buffer += 512;	}}/* * We only seem to get interrupts after an error. * Just take the interrupt and clear out the status reg. */static voidmcd_interrupt(int unused){	int st;	st = inb(MCDPORT(1)) & 0xFF;	if (st != 0xFF)	{		st = inb(MCDPORT(0)) & 0xFF;#if 0		printk("<int-%02X>", st);#endif	}}/* * I/O request routine called from Linux kernel. */static voiddo_mcd_request(void){	unsigned int block,dev;	unsigned int nsect;repeat:	if (!(CURRENT) || CURRENT->dev < 0) return;	INIT_REQUEST;	dev = MINOR(CURRENT->dev);	block = CURRENT->sector;	nsect = CURRENT->nr_sectors;	if (CURRENT == NULL || CURRENT -> sector == -1)		return;	if (CURRENT -> cmd != READ)	{		printk("mcd: bad cmd %d\n", CURRENT -> cmd);		end_request(0);		goto repeat;	}	mcd_transfer();	/* if we satisfied the request from the buffer, we're done. */	if (CURRENT -> nr_sectors == 0)	{		end_request(1);		goto repeat;	}	McdTries = MCD_RETRY_ATTEMPTS;	mcd_start();}/* * Start the I/O for the cdrom. Handle retry count. */static voidmcd_start(){	if (McdTries == 0)	{		printk("mcd: read failed after %d tries\n", MCD_RETRY_ATTEMPTS);		end_request(0);		SET_TIMER(do_mcd_request, 1);	/* wait a bit, try again */		return;	}	McdTries--;	outb(0x40, MCDPORT(0));		/* get status */	McdTimeout = MCD_STATUS_DELAY;	SET_TIMER(mcd_status, 1);}/* * Called from the timer to check the results of the get-status cmd. * On success, send the set-mode command. */static voidmcd_status(){	int st;	McdTimeout--;	st = mcdStatus();	if (st == -1)	{		if (McdTimeout == 0)		{			printk("mcd: status timed out\n");			SET_TIMER(mcd_start, 1);	/* wait a bit, try again */			return;		}

⌨️ 快捷键说明

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