📄 mcdx.c
字号:
/* audio status? */
if (stuffp->audiostatus == CDROM_AUDIO_INVALID)
stuffp->audiostatus =
e_audiobusy(st) ? CDROM_AUDIO_PLAY :
CDROM_AUDIO_NO_STATUS;
else if (stuffp->audiostatus == CDROM_AUDIO_PLAY
&& e_audiobusy(st) == 0)
stuffp->audiostatus = CDROM_AUDIO_COMPLETED;
/* media change? */
if (e_changed(st)) {
xinfo("talk() media changed\n");
stuffp->xxx = stuffp->yyy = 1;
}
/* now actually get the data */
while (sz--) {
if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) {
xinfo("talk() %02x timed out (data), %d tr%s left\n",
cmd[0], tries - 1,
tries == 2 ? "y" : "ies");
st = -1;
break;
}
if (!discard)
bp++;
xtrace(TALK, "talk() got 0x%02x\n", *(bp - 1));
}
}
#if !MCDX_QUIET
if (!tries && st == -1)
xinfo("talk() giving up\n");
#endif
stuffp->lock = 0;
wake_up_interruptible(&stuffp->lockq);
xtrace(TALK, "talk() done with 0x%02x\n", st);
return st;
}
/* MODULE STUFF ***********************************************************/
EXPORT_NO_SYMBOLS;
int __mcdx_init(void)
{
int i;
int drives = 0;
mcdx_init();
for (i = 0; i < MCDX_NDRIVES; i++) {
if (mcdx_stuffp[i]) {
xtrace(INIT, "init_module() drive %d stuff @ %p\n",
i, mcdx_stuffp[i]);
drives++;
}
}
if (!drives)
return -EIO;
return 0;
}
void __exit mcdx_exit(void)
{
int i;
xinfo("cleanup_module called\n");
for (i = 0; i < MCDX_NDRIVES; i++) {
struct s_drive_stuff *stuffp;
if (unregister_cdrom(&mcdx_info)) {
printk(KERN_WARNING "Can't unregister cdrom mcdx\n");
return;
}
stuffp = mcdx_stuffp[i];
if (!stuffp)
continue;
release_region((unsigned long) stuffp->wreg_data,
MCDX_IO_SIZE);
free_irq(stuffp->irq, NULL);
if (stuffp->toc) {
xtrace(MALLOC, "cleanup_module() free toc @ %p\n",
stuffp->toc);
kfree(stuffp->toc);
}
xtrace(MALLOC, "cleanup_module() free stuffp @ %p\n",
stuffp);
mcdx_stuffp[i] = NULL;
kfree(stuffp);
}
if (devfs_unregister_blkdev(MAJOR_NR, "mcdx") != 0) {
xwarn("cleanup() unregister_blkdev() failed\n");
}
blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
#if !MCDX_QUIET
else
xinfo("cleanup() succeeded\n");
#endif
}
#ifdef MODULE
module_init(__mcdx_init);
#endif
module_exit(mcdx_exit);
/* Support functions ************************************************/
int __init mcdx_init_drive(int drive)
{
struct s_version version;
struct s_drive_stuff *stuffp;
int size = sizeof(*stuffp);
char msg[80];
mcdx_blocksizes[drive] = 0;
xtrace(INIT, "init() try drive %d\n", drive);
xtrace(INIT, "kmalloc space for stuffpt's\n");
xtrace(MALLOC, "init() malloc %d bytes\n", size);
if (!(stuffp = kmalloc(size, GFP_KERNEL))) {
xwarn("init() malloc failed\n");
return 1;
}
xtrace(INIT, "init() got %d bytes for drive stuff @ %p\n",
sizeof(*stuffp), stuffp);
/* set default values */
memset(stuffp, 0, sizeof(*stuffp));
stuffp->present = 0; /* this should be 0 already */
stuffp->toc = NULL; /* this should be NULL already */
/* setup our irq and i/o addresses */
stuffp->irq = irq(mcdx_drive_map[drive]);
stuffp->wreg_data = stuffp->rreg_data =
port(mcdx_drive_map[drive]);
stuffp->wreg_reset = stuffp->rreg_status = stuffp->wreg_data + 1;
stuffp->wreg_hcon = stuffp->wreg_reset + 1;
stuffp->wreg_chn = stuffp->wreg_hcon + 1;
init_waitqueue_head(&stuffp->busyq);
init_waitqueue_head(&stuffp->lockq);
init_waitqueue_head(&stuffp->sleepq);
/* check if i/o addresses are available */
if (check_region((unsigned int) stuffp->wreg_data, MCDX_IO_SIZE)) {
xwarn("0x%3p,%d: Init failed. "
"I/O ports (0x%3p..0x%3p) already in use.\n",
stuffp->wreg_data, stuffp->irq,
stuffp->wreg_data,
stuffp->wreg_data + MCDX_IO_SIZE - 1);
xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp);
kfree(stuffp);
xtrace(INIT, "init() continue at next drive\n");
return 0; /* next drive */
}
xtrace(INIT, "init() i/o port is available at 0x%3p\n",
stuffp->wreg_data);
xtrace(INIT, "init() hardware reset\n");
mcdx_reset(stuffp, HARD, 1);
xtrace(INIT, "init() get version\n");
if (-1 == mcdx_requestversion(stuffp, &version, 4)) {
/* failed, next drive */
xwarn("%s=0x%3p,%d: Init failed. Can't get version.\n",
MCDX, stuffp->wreg_data, stuffp->irq);
xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp);
kfree(stuffp);
xtrace(INIT, "init() continue at next drive\n");
return 0;
}
switch (version.code) {
case 'D':
stuffp->readcmd = READ2X;
stuffp->present = DOUBLE | DOOR | MULTI;
break;
case 'F':
stuffp->readcmd = READ1X;
stuffp->present = SINGLE | DOOR | MULTI;
break;
case 'M':
stuffp->readcmd = READ1X;
stuffp->present = SINGLE;
break;
default:
stuffp->present = 0;
break;
}
stuffp->playcmd = READ1X;
if (!stuffp->present) {
xwarn("%s=0x%3p,%d: Init failed. No Mitsumi CD-ROM?.\n",
MCDX, stuffp->wreg_data, stuffp->irq);
kfree(stuffp);
return 0; /* next drive */
}
xtrace(INIT, "init() register blkdev\n");
if (devfs_register_blkdev(MAJOR_NR, "mcdx", &mcdx_bdops) != 0) {
xwarn("%s=0x%3p,%d: Init failed. Can't get major %d.\n",
MCDX, stuffp->wreg_data, stuffp->irq, MAJOR_NR);
kfree(stuffp);
return 1;
}
blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
read_ahead[MAJOR_NR] = READ_AHEAD;
blksize_size[MAJOR_NR] = mcdx_blocksizes;
xtrace(INIT, "init() subscribe irq and i/o\n");
mcdx_irq_map[stuffp->irq] = stuffp;
if (request_irq(stuffp->irq, mcdx_intr, SA_INTERRUPT, "mcdx", NULL)) {
xwarn("%s=0x%3p,%d: Init failed. Can't get irq (%d).\n",
MCDX, stuffp->wreg_data, stuffp->irq, stuffp->irq);
stuffp->irq = 0;
blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
kfree(stuffp);
return 0;
}
request_region((unsigned int) stuffp->wreg_data,
MCDX_IO_SIZE, "mcdx");
xtrace(INIT, "init() get garbage\n");
{
int i;
mcdx_delay(stuffp, HZ / 2);
for (i = 100; i; i--)
(void) inb((unsigned int) stuffp->rreg_status);
}
#if WE_KNOW_WHY
/* irq 11 -> channel register */
outb(0x50, (unsigned int) stuffp->wreg_chn);
#endif
xtrace(INIT, "init() set non dma but irq mode\n");
mcdx_config(stuffp, 1);
stuffp->minor = drive;
sprintf(msg, " mcdx: Mitsumi CD-ROM installed at 0x%3p, irq %d."
" (Firmware version %c %x)\n",
stuffp->wreg_data, stuffp->irq, version.code, version.ver);
mcdx_stuffp[drive] = stuffp;
xtrace(INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp);
mcdx_info.dev = MKDEV(MAJOR_NR, 0);
if (register_cdrom(&mcdx_info) != 0) {
printk("Cannot register Mitsumi CD-ROM!\n");
release_region((unsigned long) stuffp->wreg_data,
MCDX_IO_SIZE);
free_irq(stuffp->irq, NULL);
kfree(stuffp);
if (devfs_unregister_blkdev(MAJOR_NR, "mcdx") != 0)
xwarn("cleanup() unregister_blkdev() failed\n");
blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
return 2;
}
devfs_plain_cdrom(&mcdx_info, &mcdx_bdops);
printk(msg);
return 0;
}
int __init mcdx_init(void)
{
int drive;
#ifdef MODULE
xwarn("Version 2.14(hs) for " UTS_RELEASE "\n");
#else
xwarn("Version 2.14(hs) \n");
#endif
xwarn("$Id: mcdx.c,v 1.21 1997/01/26 07:12:59 davem Exp $\n");
/* zero the pointer array */
for (drive = 0; drive < MCDX_NDRIVES; drive++)
mcdx_stuffp[drive] = NULL;
/* do the initialisation */
for (drive = 0; drive < MCDX_NDRIVES; drive++) {
switch (mcdx_init_drive(drive)) {
case 2:
return -EIO;
case 1:
break;
}
}
return 0;
}
static int mcdx_transfer(struct s_drive_stuff *stuffp,
char *p, int sector, int nr_sectors)
/* This seems to do the actually transfer. But it does more. It
keeps track of errors occurred and will (if possible) fall back
to single speed on error.
Return: -1 on timeout or other error
else status byte (as in stuff->st) */
{
int ans;
ans = mcdx_xfer(stuffp, p, sector, nr_sectors);
return ans;
#if FALLBACK
if (-1 == ans)
stuffp->readerrs++;
else
return ans;
if (stuffp->readerrs && stuffp->readcmd == READ1X) {
xwarn("XXX Already reading 1x -- no chance\n");
return -1;
}
xwarn("XXX Fallback to 1x\n");
stuffp->readcmd = READ1X;
return mcdx_transfer(stuffp, p, sector, nr_sectors);
#endif
}
static int mcdx_xfer(struct s_drive_stuff *stuffp,
char *p, int sector, int nr_sectors)
/* This does actually the transfer from the drive.
Return: -1 on timeout or other error
else status byte (as in stuff->st) */
{
int border;
int done = 0;
long timeout;
if (stuffp->audio) {
xwarn("Attempt to read from audio CD.\n");
return -1;
}
if (!stuffp->readcmd) {
xinfo("Can't transfer from missing disk.\n");
return -1;
}
while (stuffp->lock) {
interruptible_sleep_on(&stuffp->lockq);
}
if (stuffp->valid && (sector >= stuffp->pending)
&& (sector < stuffp->low_border)) {
/* All (or at least a part of the sectors requested) seems
* to be already requested, so we don't need to bother the
* drive with new requests ...
* Wait for the drive become idle, but first
* check for possible occurred errors --- the drive
* seems to report them asynchronously */
border = stuffp->high_border < (border =
sector + nr_sectors)
? stuffp->high_border : border;
stuffp->lock = current->pid;
do {
while (stuffp->busy) {
timeout =
interruptible_sleep_on_timeout
(&stuffp->busyq, 5 * HZ);
if (!stuffp->introk) {
xtrace(XFER,
"error via interrupt\n");
} else if (!timeout) {
xtrace(XFER, "timeout\n");
} else if (signal_pending(current)) {
xtrace(XFER, "signal\n");
} else
continue;
stuffp->lock = 0;
stuffp->busy = 0;
stuffp->valid = 0;
wake_up_interruptible(&stuffp->lockq);
xtrace(XFER, "transfer() done (-1)\n");
return -1;
}
/* check if we need to set the busy flag (as we
* expect an interrupt */
stuffp->busy = (3 == (stuffp->pending & 3));
/* Test if it's the first sector of a block,
* there we have to skip some bytes as we read raw data */
if (stuffp->xa && (0 == (stuffp->pending & 3))) {
const int HEAD =
CD_FRAMESIZE_RAW - CD_XA_TAIL -
CD_FRAMESIZE;
insb((unsigned int) stuffp->rreg_data, p,
HEAD);
}
/* now actually read the data */
insb((unsigned int) stuffp->rreg_data, p, 512);
/* test if it's the last sector of a block,
* if so, we have to handle XA special */
if ((3 == (stuffp->pending & 3)) && stuffp->xa) {
char dummy[CD_XA_TAIL];
insb((unsigned int) stuffp->rreg_data,
&dummy[0], CD_XA_TAIL);
}
if (stuffp->pending == sector) {
p += 512;
done++;
sector++;
}
} while (++(stuffp->pending) < border);
stuffp->lock = 0;
wake_up_interruptible(&stuffp->lockq);
} else {
/* The requested sector(s) is/are out of the
* already requested range, so we have to bother the drive
* with a new request. */
static unsigned char cmd[] = {
0,
0, 0, 0,
0, 0, 0
};
cmd[0] = stuffp->readcmd;
/* The numbers held in ->pending, ..., should be valid */
stuffp->valid = 1;
stuffp->pending = sector & ~3;
/* do some sanity checks */
if (stuffp->pending > stuffp->lastsector) {
xwarn
("transfer() sector %d from nirvana requested.\n",
stuffp->pending);
stuffp->status = MCDX_ST_EOM;
stuffp->valid = 0;
xtrace(XFER, "transfer() done (-1)\n");
return -1;
}
if ((stuffp->low_border = stuffp->pending + DIRECT_SIZE)
> stuffp->lastsector + 1) {
xtrace(XFER, "cut low_border\n");
stuffp->low_border = stuffp->lastsector + 1;
}
if ((stuffp->high_border = stuffp->pending + REQUEST_SIZE)
> stuffp->lastsector + 1) {
xtrace(XFER, "cut high_border\n");
stuffp->high_border = stuffp->lastsector + 1;
}
{ /* Convert the sector to be requested to MSF format */
struct cdrom_msf0 pending;
log2msf(stuffp->pending / 4, &pending);
cmd[1] = pending.minute;
cmd[2] = pending.second;
cmd[3] = pending.frame;
}
cmd[6] =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -