Hi,
I have developed a patch for the Linux Mitsumi CD-ROM driver. Basically only
some unused status bits changed in the march version. So i think my patch
should continue to work with the other drivers. I don't have verified this
however. The patch was developed for linux 0.99pl11.
------------------------------------------------------------------------
/*
linux/kernel/blk_drv/mcd.c - Mitsumi CDROM driver
Copyright (C) 1992 Martin Harriss
mar...@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
(stu...@cc4.kuleuven.ac.be)
*/
#include <linux/config.h>
#ifdef CONFIG_BLK_DEV_MCD
#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>
/* #define REALLY_SLOW_IO */
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#define MAJOR_NR 23
#include "blk.h"
/* *** change this to set the I/O port address */
#define MCDPORT(x) (0x300 + (x))
/* *** change this to set the interrupt number */
#define MCD_INTR_NR 10
/* status bits */
#define MST_CMD_CHECK 0x01 /* command error */
#define MST_BUSY 0x02 /* now playing */
#define MST_READ_ERR 0x04 /* read error */
#define MST_DSK_TYPE 0x08
#define MST_SERVO_CHECK 0x10
#define MST_DSK_CHG 0x20 /* disk removed or changed */
#define MST_READY 0x40 /* disk in the drive */
#define MST_DOOR_OPEN 0x80 /* door is open */
/* flag bits */
#define MFL_DATA 0x02 /* data available */
#define MFL_STATUS 0x04 /* status available */
/* commands */
#define MCMD_GET_DISK_INFO 0x10 /* read info from disk */
#define MCMD_GET_Q_CHANNEL 0x20 /* read info from q channel */
#define MCMD_GET_STATUS 0x40
#define MCMD_SET_MODE 0x50
#define MCMD_SOFT_RESET 0x60
#define MCMD_STOP 0x70 /* stop play */
#define MCMD_CONFIG_DRIVE 0x90
#define MCMD_SET_VOLUME 0xAE /* set audio level */
#define MCMD_PLAY_READ 0xC0 /* play or read data */
#define MCMD_GET_VERSION 0xDC
/* borrowed from hd.c */
#define READ_DATA(port, buf, nr) \
__asm__("cld;rep;insb"::"d" (port),"D" (buf),"c" (nr):"cx","di")
#define SET_TIMER(func, jifs) \
((timer_table[MCD_TIMER].expires = jiffies + jifs), \
(timer_table[MCD_TIMER].fn = func), \
(timer_active |= 1<<MCD_TIMER))
#define CLEAR_TIMER timer_active &= ~(1<<MCD_TIMER)
#define MAX_TRACKS 104
struct msf {
unsigned char min;
unsigned char sec;
unsigned char frame;
struct msf start;
struct msf end;
unsigned char first;
unsigned char last;
struct msf diskLength;
struct msf firstTrack;
unsigned char ctrl_addr;
unsigned char track;
unsigned char pointIndex;
struct msf trackTime;
struct msf diskTime;
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 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 mcd_Play;
static int audioStatus;
static char diskChanged;
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(char *p);
static int bcd2bin(unsigned char bcd);
static int mcdStatus(void);
static void sendMcdCmd(int cmd, struct mcd_Play *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(char *result);
/*
* 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 < 3; retry++)
{
outb(MCMD_GET_STATUS, MCDPORT(0)); /* send get-status cmd */
st = getMcdStatus(100);
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 *arg)
{
int retry, st;
for (retry = 0; retry < 3; retry++)
{
sendMcdCmd(MCMD_PLAY_READ, arg);
st = getMcdStatus(200);
if (st != -1)
break;
}
return st;
msf2hsg(struct msf *mp)
{
return bcd2bin(mp -> frame)
+ bcd2bin(mp -> sec) * 75
+ bcd2bin(mp -> min) * 4500
- 150;
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(100);
/* 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(100);
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)
return -EINVAL;
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_sec0);
bin2bcd(&msf.cdmsf_frame0);
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);
...
read more »