patch-1.3.36 linux/drivers/cdrom/mcd.c
Next file: linux/drivers/cdrom/mcdx.c
Previous file: linux/drivers/cdrom/gscd.c
Back to the patch index
Back to the overall index
- Lines: 1633
- Date:
Sat Oct 21 19:43:31 1995
- Orig file:
v1.3.35/linux/drivers/cdrom/mcd.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v1.3.35/linux/drivers/cdrom/mcd.c linux/drivers/cdrom/mcd.c
@@ -0,0 +1,1632 @@
+/*
+ linux/kernel/blk_drv/mcd.c - Mitsumi CDROM driver
+
+ Copyright (C) 1992 Martin Harriss
+
+ martin@bdsi.com (no longer valid - where are you now, Martin?)
+
+ 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 ioctls 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>)
+
+ October 1993 Bernd Huebner and Ruediger Helsch, Unifix Software GmbH,
+ Braunschweig, Germany: rework to speed up data read operation.
+ Also enabled definition of irq and address from bootstrap, using the
+ environment.
+ November 93 added code for FX001 S,D (single & double speed).
+ February 94 added code for broken M 5/6 series of 16-bit single speed.
+
+
+ 0.4
+ Added support for loadable MODULEs, so mcd can now also be loaded by
+ insmod and removed by rmmod during runtime.
+ Werner Zimmermann (zimmerma@rz.fht-esslingen.de), Mar. 26, 95
+
+ 0.5
+ I added code for FX001 D to drop from double speed to single speed
+ when encountering errors... this helps with some "problematic" CD's
+ that are supposedly "OUT OF TOLERANCE" (but are really shitty presses!)
+ severly scratched, or possibly slightly warped! I have noticed that
+ the Mitsumi 2x/4x drives are just less tolerant and the firmware is
+ not smart enough to drop speed, so let's just kludge it with software!
+ ****** THE 4X SPEED MITSUMI DRIVES HAVE THE SAME PROBLEM!!!!!! ******
+ Anyone want to "DONATE" one to me?! ;) I hear sometimes they are
+ even WORSE! ;)
+ ** HINT... HINT... TAKE NOTES MITSUMI This could save some hassels with
+ certain "large" CD's that have data on the outside edge in your
+ DOS DRIVERS .... Accuracy counts... speed is secondary ;)
+ 17 June 95 Modifications By Andrew J. Kroll <ag784@freenet.buffalo.edu>
+ 07 July 1995 Modifications by Andrew J. Kroll
+
+*/
+
+#include <linux/config.h>
+
+#ifdef MODULE
+# include <linux/module.h>
+# include <linux/version.h>
+# ifndef CONFIG_MODVERSIONS
+ char kernel_version[]= UTS_RELEASE;
+# endif
+#define mcd_init init_module
+#else
+# define MOD_INC_USE_COUNT
+# define MOD_DEC_USE_COUNT
+#endif
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+
+/* #define REALLY_SLOW_IO */
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR MITSUMI_CDROM_MAJOR
+#include <linux/blk.h>
+#define mcd_port mcd /* for compatible parameter passing with "insmod" */
+#include <linux/mcd.h>
+
+#if 0
+static int mcd_sizes[] = { 0 };
+#endif
+
+/* I know putting defines in this file is probably stupid, but it should be */
+/* the only place that they are really needed... I HOPE! :) */
+
+/* How many sectors to read at 1x when an error at 2x speed occurs. */
+/* You can change this to anything from 2 to 32767, but 30 seems to */
+/* work best for me. I have found that when the drive has problems */
+/* reading one sector, it will have troubles reading the next few. */
+#define SINGLE_HOLD_SECTORS 30
+
+#define MCMD_2X_READ 0xC1 /* Double Speed Read DON'T TOUCH! */
+
+/* I added A flag to drop to 1x speed if too many errors 0 = 1X ; 1 = 2X */
+static int mcdDouble = 0;
+
+/* How many sectors to hold at 1x speed counter */
+static int mcd1xhold = 0;
+
+/* Is the drive connected properly and responding?? */
+static int mcdPresent = 0;
+
+#if 0
+#define TEST1 /* <int-..> */
+#define TEST2 /* do_mcd_req */
+#define TEST3 */ /* MCD_S_state */
+#define TEST4 /* QUICK_LOOP-counter */
+#define TEST5 */ /* port(1) state */
+#endif
+
+#if 1
+#define QUICK_LOOP_DELAY udelay(45) /* use udelay */
+#define QUICK_LOOP_COUNT 20
+#else
+#define QUICK_LOOP_DELAY
+#define QUICK_LOOP_COUNT 140 /* better wait constant time */
+#endif
+/* #define DOUBLE_QUICK_ONLY */
+
+#define CURRENT_VALID \
+ (CURRENT && MAJOR(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \
+ && CURRENT -> sector != -1)
+#define MFL_STATUSorDATA (MFL_STATUS | MFL_DATA)
+#define MCD_BUF_SIZ 16
+static volatile int mcd_transfer_is_active;
+static char mcd_buf[2048*MCD_BUF_SIZ]; /* buffer for block size conversion */
+static volatile int mcd_buf_bn[MCD_BUF_SIZ], mcd_next_bn;
+static volatile int mcd_buf_in, mcd_buf_out = -1;
+static volatile int mcd_error;
+static int mcd_open_count;
+enum mcd_state_e {
+ MCD_S_IDLE, /* 0 */
+ MCD_S_START, /* 1 */
+ MCD_S_MODE, /* 2 */
+ MCD_S_READ, /* 3 */
+ MCD_S_DATA, /* 4 */
+ MCD_S_STOP, /* 5 */
+ MCD_S_STOPPING /* 6 */
+};
+static volatile enum mcd_state_e mcd_state = MCD_S_IDLE;
+static int mcd_mode = -1;
+static int MCMD_DATA_READ= MCMD_PLAY_READ;
+#define READ_TIMEOUT 3000
+#define WORK_AROUND_MITSUMI_BUG_92
+#define WORK_AROUND_MITSUMI_BUG_93
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+int mitsumi_bug_93_wait = 0;
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
+
+static short mcd_port = MCD_BASE_ADDR; /* used as "mcd" by "insmod" */
+static int mcd_irq = MCD_INTR_NR; /* must directly follow mcd_port */
+
+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_poll(void);
+static void mcd_invalidate_buffers(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];
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+ if (ints[0] > 2)
+ mitsumi_bug_93_wait = ints[3];
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
+}
+
+
+static int
+check_mcd_change(kdev_t full_dev)
+{
+ 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;
+ 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;
+ struct cdrom_volctrl volctrl;
+
+ 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 */
+ st = verify_area(VERIFY_READ, (void *) arg, sizeof(volctrl));
+ if (st)
+ return st;
+
+ memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
+ outb(MCMD_SET_VOLUME, MCDPORT(0));
+ outb(volctrl.channel0, MCDPORT(0));
+ outb(255, MCDPORT(0));
+ outb(volctrl.channel1, MCDPORT(0));
+ outb(255, MCDPORT(0));
+
+ i = getMcdStatus(MCD_STATUS_DELAY);
+ if (i < 0)
+ return -EIO;
+
+ {
+ char a, b, c, d;
+
+ getValue(&a);
+ getValue(&b);
+ getValue(&c);
+ getValue(&d);
+ }
+
+ return 0;
+
+ case CDROMEJECT:
+ /* all drives can at least stop! */
+ if (audioStatus == CDROM_AUDIO_PLAY) {
+ outb(MCMD_STOP, MCDPORT(0));
+ i = getMcdStatus(MCD_STATUS_DELAY);
+ }
+
+ audioStatus = CDROM_AUDIO_NO_STATUS;
+
+ outb(MCMD_EJECT, MCDPORT(0));
+ /*
+ * the status (i) shows failure on all but the FX drives.
+ * But nothing we can do about that in software!
+ * So just read the status and forget it. - Jon.
+ */
+ i = getMcdStatus(MCD_STATUS_DELAY);
+ 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)
+{
+ if (CURRENT_VALID) {
+ while (CURRENT -> nr_sectors) {
+ int bn = CURRENT -> sector / 4;
+ int i;
+ for (i = 0; i < MCD_BUF_SIZ && mcd_buf_bn[i] != bn; ++i)
+ ;
+ if (i < MCD_BUF_SIZ) {
+ int offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
+ int nr_sectors = 4 - (CURRENT -> sector & 3);
+ if (mcd_buf_out != i) {
+ mcd_buf_out = i;
+ if (mcd_buf_bn[i] != bn) {
+ mcd_buf_out = -1;
+ continue;
+ }
+ }
+ if (nr_sectors > CURRENT -> nr_sectors)
+ nr_sectors = CURRENT -> nr_sectors;
+ memcpy(CURRENT -> buffer, mcd_buf + offs, nr_sectors * 512);
+ CURRENT -> nr_sectors -= nr_sectors;
+ CURRENT -> sector += nr_sectors;
+ CURRENT -> buffer += nr_sectors * 512;
+ } else {
+ mcd_buf_out = -1;
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * We only seem to get interrupts after an error.
+ * Just take the interrupt and clear out the status reg.
+ */
+
+static void
+mcd_interrupt(int irq, struct pt_regs * regs)
+{
+ int st;
+
+ st = inb(MCDPORT(1)) & 0xFF;
+#ifdef TEST1
+ printk("<int1-%02X>", st);
+#endif
+ if (!(st & MFL_STATUS))
+ {
+ st = inb(MCDPORT(0)) & 0xFF;
+#ifdef TEST1
+ printk("<int0-%02X>", st);
+#endif
+ if ((st & 0xFF) != 0xFF)
+ mcd_error = st ? st & 0xFF : -1;
+ }
+}
+
+
+static void
+do_mcd_request(void)
+{
+#ifdef TEST2
+ printk(" do_mcd_request(%ld+%ld)\n", CURRENT -> sector, CURRENT -> nr_sectors);
+#endif
+ mcd_transfer_is_active = 1;
+ while (CURRENT_VALID) {
+ if (CURRENT->bh) {
+ if (!CURRENT->bh->b_lock)
+ panic(DEVICE_NAME ": block not locked");
+ }
+ mcd_transfer();
+ if (CURRENT -> nr_sectors == 0) {
+ end_request(1);
+ } else {
+ mcd_buf_out = -1; /* Want to read a block not in buffer */
+ if (mcd_state == MCD_S_IDLE) {
+ if (!tocUpToDate) {
+ if (updateToc() < 0) {
+ while (CURRENT_VALID)
+ end_request(0);
+ break;
+ }
+ }
+ mcd_state = MCD_S_START;
+ McdTries = 5;
+ SET_TIMER(mcd_poll, 1);
+ }
+ break;
+ }
+ }
+ mcd_transfer_is_active = 0;
+#ifdef TEST2
+ printk(" do_mcd_request ends\n");
+#endif
+}
+
+
+
+static void
+mcd_poll(void)
+{
+ int st;
+
+
+ if (mcd_error)
+ {
+ if (mcd_error & 0xA5)
+ {
+ printk("mcd: I/O error 0x%02x", mcd_error);
+ if (mcd_error & 0x80)
+ printk(" (Door open)");
+ if (mcd_error & 0x20)
+ printk(" (Disk changed)");
+ if (mcd_error & 0x04)
+ {
+ printk(" (Read error)"); /* Bitch about the problem. */
+
+ /* Time to get fancy! If at 2x speed and 1 error, drop to 1x speed! */
+ /* Interesting how it STAYS at MCD_RETRY_ATTEMPTS on first error! */
+ /* But I find that rather HANDY!!! */
+ /* Neat! it REALLY WORKS on those LOW QUALITY CD's!!! Smile! :) */
+ /* AJK [06/17/95] */
+
+ /* Slap the CD down to single speed! */
+ if (mcdDouble == 1 && McdTries == MCD_RETRY_ATTEMPTS && MCMD_DATA_READ == MCMD_2X_READ)
+ {
+ MCMD_DATA_READ = MCMD_PLAY_READ; /* Uhhh, Ummmm, muhuh-huh! */
+ mcd1xhold = SINGLE_HOLD_SECTORS; /* Hey Bevis! */
+ printk(" Speed now 1x"); /* Pull my finger! */
+ }
+ }
+ printk("\n");
+ mcd_invalidate_buffers();
+#ifdef WARN_IF_READ_FAILURE
+ if (McdTries == MCD_RETRY_ATTEMPTS)
+ printk("mcd: read of block %d failed\n", mcd_next_bn);
+#endif
+ if (!McdTries--)
+ {
+ /* Nuts! This cd is ready for recycling! */
+ /* When WAS the last time YOU cleaned it CORRECTLY?! */
+ printk("mcd: read of block %d failed, giving up\n", mcd_next_bn);
+ if (mcd_transfer_is_active)
+ {
+ McdTries = 0;
+ goto ret;
+ }
+ if (CURRENT_VALID)
+ end_request(0);
+ McdTries = MCD_RETRY_ATTEMPTS;
+ }
+ }
+ mcd_error = 0;
+ mcd_state = MCD_S_STOP;
+ }
+ /* Switch back to Double speed if enough GOOD sectors were read! */
+
+ /* Are we a double speed with a crappy CD?! */
+ if (mcdDouble == 1 && McdTries == MCD_RETRY_ATTEMPTS && MCMD_DATA_READ == MCMD_PLAY_READ)
+ {
+ /* We ARE a double speed and we ARE bitching! */
+ if (mcd1xhold == 0) /* Okay, Like are we STILL at single speed? */
+ { /* We need to switch back to double speed now... */
+ MCMD_DATA_READ = MCMD_2X_READ; /* Uhhh... BACK You GO! */
+ printk("mcd: Switching back to 2X speed!\n"); /* Tell 'em! */
+ }
+ else mcd1xhold--; /* No?! Count down the good reads some more... */
+ /* and try, try again! */
+ }
+
+
+
+ immediately:
+ switch (mcd_state) {
+
+
+
+ case MCD_S_IDLE:
+#ifdef TEST3
+ printk("MCD_S_IDLE\n");
+#endif
+ return;
+
+
+
+ case MCD_S_START:
+#ifdef TEST3
+ printk("MCD_S_START\n");
+#endif
+
+ outb(MCMD_GET_STATUS, MCDPORT(0));
+ mcd_state = mcd_mode == 1 ? MCD_S_READ : MCD_S_MODE;
+ McdTimeout = 3000;
+ break;
+
+
+
+ case MCD_S_MODE:
+#ifdef TEST3
+ printk("MCD_S_MODE\n");
+#endif
+
+ if ((st = mcdStatus()) != -1) {
+
+ if (st & MST_DSK_CHG) {
+ mcdDiskChanged = 1;
+ tocUpToDate = 0;
+ mcd_invalidate_buffers();
+ }
+
+ set_mode_immediately:
+
+ if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) {
+ mcdDiskChanged = 1;
+ tocUpToDate = 0;
+ if (mcd_transfer_is_active) {
+ mcd_state = MCD_S_START;
+ goto immediately;
+ }
+ printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n");
+ mcd_state = MCD_S_IDLE;
+ while (CURRENT_VALID)
+ end_request(0);
+ return;
+ }
+
+ outb(MCMD_SET_MODE, MCDPORT(0));
+ outb(1, MCDPORT(0));
+ mcd_mode = 1;
+ mcd_state = MCD_S_READ;
+ McdTimeout = 3000;
+
+ }
+ break;
+
+
+
+ case MCD_S_READ:
+#ifdef TEST3
+ printk("MCD_S_READ\n");
+#endif
+
+ if ((st = mcdStatus()) != -1) {
+
+ if (st & MST_DSK_CHG) {
+ mcdDiskChanged = 1;
+ tocUpToDate = 0;
+ mcd_invalidate_buffers();
+ }
+
+ read_immediately:
+
+ if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) {
+ mcdDiskChanged = 1;
+ tocUpToDate = 0;
+ if (mcd_transfer_is_active) {
+ mcd_state = MCD_S_START;
+ goto immediately;
+ }
+ printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n");
+ mcd_state = MCD_S_IDLE;
+ while (CURRENT_VALID)
+ end_request(0);
+ return;
+ }
+
+ if (CURRENT_VALID) {
+ struct mcd_Play_msf msf;
+ mcd_next_bn = CURRENT -> sector / 4;
+ hsg2msf(mcd_next_bn, &msf.start);
+ msf.end.min = ~0;
+ msf.end.sec = ~0;
+ msf.end.frame = ~0;
+ sendMcdCmd(MCMD_DATA_READ, &msf);
+ mcd_state = MCD_S_DATA;
+ McdTimeout = READ_TIMEOUT;
+ } else {
+ mcd_state = MCD_S_STOP;
+ goto immediately;
+ }
+
+ }
+ break;
+
+
+ case MCD_S_DATA:
+#ifdef TEST3
+ printk("MCD_S_DATA\n");
+#endif
+
+ st = inb(MCDPORT(1)) & (MFL_STATUSorDATA);
+ data_immediately:
+#ifdef TEST5
+ printk("Status %02x\n",st);
+#endif
+ switch (st) {
+
+ case MFL_DATA:
+#ifdef WARN_IF_READ_FAILURE
+ if (McdTries == 5)
+ printk("mcd: read of block %d failed\n", mcd_next_bn);
+#endif
+ if (!McdTries--) {
+ printk("mcd: read of block %d failed, giving up\n", mcd_next_bn);
+ if (mcd_transfer_is_active) {
+ McdTries = 0;
+ break;
+ }
+ if (CURRENT_VALID)
+ end_request(0);
+ McdTries = 5;
+ }
+ mcd_state = MCD_S_START;
+ McdTimeout = READ_TIMEOUT;
+ goto immediately;
+
+ case MFL_STATUSorDATA:
+ break;
+
+ default:
+ McdTries = 5;
+ if (!CURRENT_VALID && mcd_buf_in == mcd_buf_out) {
+ mcd_state = MCD_S_STOP;
+ goto immediately;
+ }
+ mcd_buf_bn[mcd_buf_in] = -1;
+ READ_DATA(MCDPORT(0), mcd_buf + 2048 * mcd_buf_in, 2048);
+ mcd_buf_bn[mcd_buf_in] = mcd_next_bn++;
+ if (mcd_buf_out == -1)
+ mcd_buf_out = mcd_buf_in;
+ mcd_buf_in = mcd_buf_in + 1 == MCD_BUF_SIZ ? 0 : mcd_buf_in + 1;
+ if (!mcd_transfer_is_active) {
+ while (CURRENT_VALID) {
+ mcd_transfer();
+ if (CURRENT -> nr_sectors == 0)
+ end_request(1);
+ else
+ break;
+ }
+ }
+
+ if (CURRENT_VALID
+ && (CURRENT -> sector / 4 < mcd_next_bn ||
+ CURRENT -> sector / 4 > mcd_next_bn + 16)) {
+ mcd_state = MCD_S_STOP;
+ goto immediately;
+ }
+ McdTimeout = READ_TIMEOUT;
+#ifdef DOUBLE_QUICK_ONLY
+ if (MCMD_DATA_READ != MCMD_PLAY_READ)
+#endif
+ {
+ int count= QUICK_LOOP_COUNT;
+ while (count--) {
+ QUICK_LOOP_DELAY;
+ if ((st = (inb(MCDPORT(1))) & (MFL_STATUSorDATA)) != (MFL_STATUSorDATA)) {
+# ifdef TEST4
+/* printk("Quickloop success at %d\n",QUICK_LOOP_COUNT-count); */
+ printk(" %d ",QUICK_LOOP_COUNT-count);
+# endif
+ goto data_immediately;
+ }
+ }
+# ifdef TEST4
+/* printk("Quickloop ended at %d\n",QUICK_LOOP_COUNT); */
+ printk("ended ");
+# endif
+ }
+ break;
+ }
+ break;
+
+
+
+ case MCD_S_STOP:
+#ifdef TEST3
+ printk("MCD_S_STOP\n");
+#endif
+
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+ if (!mitsumi_bug_93_wait)
+ goto do_not_work_around_mitsumi_bug_93_1;
+
+ McdTimeout = mitsumi_bug_93_wait;
+ mcd_state = 9+3+1;
+ break;
+
+ case 9+3+1:
+ if (McdTimeout)
+ break;
+
+ do_not_work_around_mitsumi_bug_93_1:
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
+
+ outb(MCMD_STOP, MCDPORT(0));
+
+#ifdef WORK_AROUND_MITSUMI_BUG_92
+ if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) {
+ int i = 4096;
+ do {
+ inb(MCDPORT(0));
+ } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i);
+ outb(MCMD_STOP, MCDPORT(0));
+ if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) {
+ i = 4096;
+ do {
+ inb(MCDPORT(0));
+ } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i);
+ outb(MCMD_STOP, MCDPORT(0));
+ }
+ }
+#endif /* WORK_AROUND_MITSUMI_BUG_92 */
+
+ mcd_state = MCD_S_STOPPING;
+ McdTimeout = 1000;
+ break;
+
+ case MCD_S_STOPPING:
+#ifdef TEST3
+ printk("MCD_S_STOPPING\n");
+#endif
+
+ if ((st = mcdStatus()) == -1 && McdTimeout)
+ break;
+
+ if ((st != -1) && (st & MST_DSK_CHG)) {
+ mcdDiskChanged = 1;
+ tocUpToDate = 0;
+ mcd_invalidate_buffers();
+ }
+
+#ifdef WORK_AROUND_MITSUMI_BUG_93
+ if (!mitsumi_bug_93_wait)
+ goto do_not_work_around_mitsumi_bug_93_2;
+
+ McdTimeout = mitsumi_bug_93_wait;
+ mcd_state = 9+3+2;
+ break;
+
+ case 9+3+2:
+ if (McdTimeout)
+ break;
+
+ st = -1;
+
+ do_not_work_around_mitsumi_bug_93_2:
+#endif /* WORK_AROUND_MITSUMI_BUG_93 */
+
+#ifdef TEST3
+ printk("CURRENT_VALID %d mcd_mode %d\n",
+ CURRENT_VALID, mcd_mode);
+#endif
+
+ if (CURRENT_VALID) {
+ if (st != -1) {
+ if (mcd_mode == 1)
+ goto read_immediately;
+ else
+ goto set_mode_immediately;
+ } else {
+ mcd_state = MCD_S_START;
+ McdTimeout = 1;
+ }
+ } else {
+ mcd_state = MCD_S_IDLE;
+ return;
+ }
+ break;
+
+ default:
+ printk("mcd: invalid state %d\n", mcd_state);
+ return;
+ }
+
+ ret:
+ if (!McdTimeout--) {
+ printk("mcd: timeout in state %d\n", mcd_state);
+ mcd_state = MCD_S_STOP;
+ }
+
+ SET_TIMER(mcd_poll, 1);
+}
+
+
+
+static void
+mcd_invalidate_buffers(void)
+{
+ int i;
+ for (i = 0; i < MCD_BUF_SIZ; ++i)
+ mcd_buf_bn[i] = -1;
+ mcd_buf_out = -1;
+}
+
+
+/*
+ * Open the device special file. Check that a disk is in.
+ */
+
+int
+mcd_open(struct inode *ip, struct file *fp)
+{
+ int st;
+
+ if (mcdPresent == 0)
+ return -ENXIO; /* no hardware */
+
+ if (fp->f_mode & 2) /* write access? */
+ return -EROFS;
+
+ if (!mcd_open_count && mcd_state == MCD_S_IDLE) {
+
+ mcd_invalidate_buffers();
+
+ st = statusCmd(); /* check drive status */
+ if (st == -1)
+ return -EIO; /* drive doesn't respond */
+
+ if ((st & MST_READY) == 0) /* no disk in drive */
+ {
+ printk("mcd: no disk in drive\n");
+ return -EIO;
+ }
+
+ if (updateToc() < 0)
+ return -EIO;
+
+ }
+ ++mcd_open_count;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+
+/*
+ * On close, we flush all mcd blocks from the buffer cache.
+ */
+
+static void
+mcd_release(struct inode * inode, struct file * file)
+{ MOD_DEC_USE_COUNT;
+ if (!--mcd_open_count) {
+ mcd_invalidate_buffers();
+ sync_dev(inode->i_rdev);
+ invalidate_buffers(inode -> i_rdev);
+ }
+}
+
+
+static struct file_operations mcd_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ mcd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ mcd_open, /* open */
+ mcd_release, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ check_mcd_change, /* media change */
+ NULL /* revalidate */
+};
+
+
+/*
+ * Test for presence of drive and initialize it. Called at boot time.
+ */
+
+int
+mcd_init(void)
+{
+ int count;
+ unsigned char result[3];
+
+ if (mcd_port <= 0 || mcd_irq <= 0) {
+ printk("skip mcd_init\n");
+ return -EIO;
+ }
+
+ printk("mcd=0x%x,%d: ", mcd_port, mcd_irq);
+
+ if (register_blkdev(MAJOR_NR, "mcd", &mcd_fops) != 0)
+ {
+ printk("Unable to get major %d for Mitsumi CD-ROM\n",
+ MAJOR_NR);
+ return -EIO;
+ }
+
+ if (check_region(mcd_port, 4)) {
+ printk("Init failed, I/O port (%X) already in use\n",
+ mcd_port);
+ return -EIO;
+ }
+
+ blk_dev[MAJOR_NR].request_fn = 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("Init failed. No mcd device at 0x%x irq %d\n",
+ mcd_port, mcd_irq);
+ 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("mitsumi get version failed at 0x%d\n",
+ mcd_port);
+ return -EIO;
+ }
+
+ if (result[0] == result[1] && result[1] == result[2])
+ return -EIO;
+ printk("Mitsumi status, type and version : %02X %c %x ",
+ result[0],result[1],result[2]);
+
+ if (result[1] == 'D')
+ {
+ printk("Double Speed CD ROM\n");
+ MCMD_DATA_READ = MCMD_2X_READ;
+ mcdDouble = 1; /* Added flag to drop to 1x speed if too many errors */
+ }
+ else printk("Single Speed CD ROM\n");
+
+ 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"))
+ {
+ printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
+ return -EIO;
+ }
+ 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;
+ 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(void)
+{
+ if (!(inb(MCDPORT(1)) & MFL_STATUS))
+ {
+ wake_up(&mcd_waitq);
+ return;
+ }
+
+ McdTimeout--;
+ if (McdTimeout <= 0)
+ {
+ wake_up(&mcd_waitq);
+ return;
+ }
+
+ SET_TIMER(mcdStatTimer, 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;
+ SET_TIMER(mcdStatTimer, 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;
+}
+
+
+/*
+ * Read a value from the drive. Should return quickly, so a busy wait
+ * is used to avoid excessive rescheduling.
+ */
+
+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(¬Used) < 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()
+{
+ 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()
+{
+ 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);
+
+ 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;
+
+#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
+
+ return 0;
+}
+
+
+/*
+ * Read the table of contents (TOC)
+ */
+
+static int
+GetToc()
+{
+ 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;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{ if (MOD_IN_USE)
+ { printk("mcd module in use - can't remove it.\n");
+ return;
+ }
+ if ((unregister_blkdev(MAJOR_NR, "mcd") == -EINVAL))
+ { printk("What's that: can't unregister mcd\n");
+ return;
+ }
+ release_region(mcd_port,4);
+ free_irq(mcd_irq);
+ printk("mcd module released.\n");
+}
+#endif MODULE
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this