📄 mcd.c
字号:
/* This routine gets called during initialization if things go wrong,
* and is used in mcd_exit as well. */
static void cleanup(int level)
{
switch (level) {
case 3:
if (unregister_cdrom(&mcd_info)) {
printk(KERN_WARNING "Can't unregister cdrom mcd\n");
return;
}
free_irq(mcd_irq, NULL);
case 2:
release_region(mcd_port, 4);
case 1:
if (devfs_unregister_blkdev(MAJOR_NR, "mcd")) {
printk(KERN_WARNING "Can't unregister major mcd\n");
return;
}
blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
default:;
}
}
/*
* Test for presence of drive and initialize it. Called at boot time.
*/
int __init mcd_init(void)
{
int count;
unsigned char result[3];
char msg[80];
if (mcd_port <= 0 || mcd_irq <= 0) {
printk(KERN_INFO "mcd: not probing.\n");
return -EIO;
}
if (devfs_register_blkdev(MAJOR_NR, "mcd", &mcd_bdops) != 0) {
printk(KERN_ERR "mcd: Unable to get major %d for Mitsumi CD-ROM\n", MAJOR_NR);
return -EIO;
}
if (check_region(mcd_port, 4)) {
cleanup(1);
printk(KERN_ERR "mcd: Initialization failed, I/O port (%X) already in use\n", mcd_port);
return -EIO;
}
blksize_size[MAJOR_NR] = mcd_blocksizes;
blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), 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(KERN_INFO "mcd: initialisation failed - No mcd device at 0x%x irq %d\n",
mcd_port, mcd_irq);
cleanup(1);
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(KERN_ERR "mcd: mitsumi get version failed at 0x%x\n",
mcd_port);
cleanup(1);
return -EIO;
}
if (result[0] == result[1] && result[1] == result[2]) {
cleanup(1);
return -EIO;
}
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", NULL)) {
printk(KERN_ERR "mcd: Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
cleanup(1);
return -EIO;
}
if (result[1] == 'D') {
MCMD_DATA_READ = MCMD_2X_READ;
/* Added flag to drop to 1x speed if too many errors */
mcdDouble = 1;
} else
mcd_info.speed = 1;
sprintf(msg, " mcd: Mitsumi %s Speed CD-ROM at port=0x%x,"
" irq=%d\n", mcd_info.speed == 1 ? "Single" : "Double",
mcd_port, mcd_irq);
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;
mcd_info.dev = MKDEV(MAJOR_NR, 0);
if (register_cdrom(&mcd_info) != 0) {
printk(KERN_ERR "mcd: Unable to register Mitsumi CD-ROM.\n");
cleanup(3);
return -EIO;
}
devfs_plain_cdrom(&mcd_info, &mcd_bdops);
printk(msg);
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(unsigned long dummy)
{
if (!(inb(MCDPORT(1)) & MFL_STATUS)) {
wake_up(&mcd_waitq);
return;
}
McdTimeout--;
if (McdTimeout <= 0) {
wake_up(&mcd_waitq);
return;
}
mcd_timer.function = mcdStatTimer;
mod_timer(&mcd_timer, jiffies + 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;
mcd_timer.function = mcdStatTimer;
mod_timer(&mcd_timer, jiffies + 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;
}
/* gives current state of the drive This function is quite unreliable,
and should probably be rewritten by someone, eventually... */
int mcd_drive_status(struct cdrom_device_info *cdi, int slot_nr)
{
int st;
st = statusCmd(); /* check drive status */
if (st == -1)
return -EIO; /* drive doesn't respond */
if ((st & MST_READY))
return CDS_DISC_OK;
if ((st & MST_DOOR_OPEN))
return CDS_TRAY_OPEN;
if ((st & MST_DSK_CHG))
return CDS_NO_DISC;
if ((st & MST_BUSY))
return CDS_DRIVE_NOT_READY;
return -EIO;
}
/*
* Read a value from the drive.
*/
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(void)
{
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(void)
{
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);
#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
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;
return 0;
}
/*
* Read the table of contents (TOC)
*/
static int GetToc(void)
{
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;
}
void __exit mcd_exit(void)
{
cleanup(3);
del_timer_sync(&mcd_timer);
}
#ifdef MODULE
module_init(mcd_init);
#endif
module_exit(mcd_exit);
MODULE_AUTHOR("Martin Harriss");
MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -