?? mcd.c
字號(hào):
/*
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 0
static int mcd_sizes[] = { 0 };
#endif
static 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];
}
int
check_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 int
statusCmd(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 int
mcdPlay(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;
}
long
msf2hsg(struct msf *mp)
{
return bcd2bin(mp -> frame)
+ bcd2bin(mp -> sec) * 75
+ bcd2bin(mp -> min) * 4500
- 150;
}
static int
mcd_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_DEBUG
printk("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_DEBUG
printk("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 void
mcd_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 void
mcd_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 void
do_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 void
mcd_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 void
mcd_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;
}
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -