📄 mcd.c
字号:
/* 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 + -