📄 mcdx.c
字号:
/*
* The Mitsumi CDROM interface
* Copyright (C) 1995 1996 Heiko Schlittermann <heiko@lotte.sax.de>
* VERSION: 2.14(hs)
*
* ... anyway, I'm back again, thanks to Marcin, he adopted
* large portions of my code (at least the parts containing
* my main thoughts ...)
*
****************** H E L P *********************************
* If you ever plan to update your CD ROM drive and perhaps
* want to sell or simply give away your Mitsumi FX-001[DS]
* -- Please --
* mail me (heiko@lotte.sax.de). When my last drive goes
* ballistic no more driver support will be available from me!
*************************************************************
*
* 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; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Thanks to
* The Linux Community at all and ...
* Martin Harriss (he wrote the first Mitsumi Driver)
* Eberhard Moenkeberg (he gave me much support and the initial kick)
* Bernd Huebner, Ruediger Helsch (Unifix-Software GmbH, they
* improved the original driver)
* Jon Tombs, Bjorn Ekwall (module support)
* Daniel v. Mosnenck (he sent me the Technical and Programming Reference)
* Gerd Knorr (he lent me his PhotoCD)
* Nils Faerber and Roger E. Wolff (extensively tested the LU portion)
* Andreas Kies (testing the mysterious hang-ups)
* Heiko Eissfeldt (VERIFY_READ/WRITE)
* Marcin Dalecki (improved performance, shortened code)
* ... somebody forgotten?
*
* 9 November 1999 -- Make kernel-parameter implementation work with 2.3.x
* Removed init_module & cleanup_module in favor of
* module_init & module_exit.
* Torben Mathiasen <tmm@image.dk>
*/
#if RCS
static const char *mcdx_c_version
= "$Id: mcdx.c,v 1.21 1997/01/26 07:12:59 davem Exp $";
#endif
#include <linux/version.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/cdrom.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/major.h>
#define MAJOR_NR MITSUMI_X_CDROM_MAJOR
#include <linux/blk.h>
#include <linux/devfs_fs_kernel.h>
/* for compatible parameter passing with "insmod" */
#define mcdx_drive_map mcdx
#include "mcdx.h"
#if BITS_PER_LONG != 32
# error FIXME: this driver only works on 32-bit platforms
#endif
#ifndef HZ
#error HZ not defined
#endif
#define xwarn(fmt, args...) printk(KERN_WARNING MCDX " " fmt, ## args)
#if !MCDX_QUIET
#define xinfo(fmt, args...) printk(KERN_INFO MCDX " " fmt, ## args)
#else
#define xinfo(fmt, args...) { ; }
#endif
#if MCDX_DEBUG
#define xtrace(lvl, fmt, args...) \
{ if (lvl > 0) \
{ printk(KERN_DEBUG MCDX ":: " fmt, ## args); } }
#define xdebug(fmt, args...) printk(KERN_DEBUG MCDX ":: " fmt, ## args)
#else
#define xtrace(lvl, fmt, args...) { ; }
#define xdebug(fmt, args...) { ; }
#endif
/* CONSTANTS *******************************************************/
/* Following are the number of sectors we _request_ from the drive
every time an access outside the already requested range is done.
The _direct_ size is the number of sectors we're allowed to skip
directly (performing a read instead of requesting the new sector
needed */
const int REQUEST_SIZE = 800; /* should be less then 255 * 4 */
const int DIRECT_SIZE = 400; /* should be less then REQUEST_SIZE */
enum drivemodes { TOC, DATA, RAW, COOKED };
enum datamodes { MODE0, MODE1, MODE2 };
enum resetmodes { SOFT, HARD };
const int SINGLE = 0x01; /* single speed drive (FX001S, LU) */
const int DOUBLE = 0x02; /* double speed drive (FX001D, ..? */
const int DOOR = 0x04; /* door locking capability */
const int MULTI = 0x08; /* multi session capability */
const unsigned char READ1X = 0xc0;
const unsigned char READ2X = 0xc1;
/* DECLARATIONS ****************************************************/
struct s_subqcode {
unsigned char control;
unsigned char tno;
unsigned char index;
struct cdrom_msf0 tt;
struct cdrom_msf0 dt;
};
struct s_diskinfo {
unsigned int n_first;
unsigned int n_last;
struct cdrom_msf0 msf_leadout;
struct cdrom_msf0 msf_first;
};
struct s_multi {
unsigned char multi;
struct cdrom_msf0 msf_last;
};
struct s_version {
unsigned char code;
unsigned char ver;
};
/* Per drive/controller stuff **************************************/
struct s_drive_stuff {
/* waitqueues */
wait_queue_head_t busyq;
wait_queue_head_t lockq;
wait_queue_head_t sleepq;
/* flags */
volatile int introk; /* status of last irq operation */
volatile int busy; /* drive performs an operation */
volatile int lock; /* exclusive usage */
/* cd infos */
struct s_diskinfo di;
struct s_multi multi;
struct s_subqcode *toc; /* first entry of the toc array */
struct s_subqcode start;
struct s_subqcode stop;
int xa; /* 1 if xa disk */
int audio; /* 1 if audio disk */
int audiostatus;
/* `buffer' control */
volatile int valid; /* pending, ..., values are valid */
volatile int pending; /* next sector to be read */
volatile int low_border; /* first sector not to be skipped direct */
volatile int high_border; /* first sector `out of area' */
#ifdef AK2
volatile int int_err;
#endif /* AK2 */
/* adds and odds */
void *wreg_data; /* w data */
void *wreg_reset; /* w hardware reset */
void *wreg_hcon; /* w hardware conf */
void *wreg_chn; /* w channel */
void *rreg_data; /* r data */
void *rreg_status; /* r status */
int irq; /* irq used by this drive */
int minor; /* minor number of this drive */
int present; /* drive present and its capabilities */
unsigned char readcmd; /* read cmd depends on single/double speed */
unsigned char playcmd; /* play should always be single speed */
unsigned int xxx; /* set if changed, reset while open */
unsigned int yyy; /* set if changed, reset by media_changed */
int users; /* keeps track of open/close */
int lastsector; /* last block accessible */
int status; /* last operation's error / status */
int readerrs; /* # of blocks read w/o error */
};
/* Prototypes ******************************************************/
/* The following prototypes are already declared elsewhere. They are
repeated here to show what's going on. And to sense, if they're
changed elsewhere. */
/* declared in blk.h */
int mcdx_init(void);
void do_mcdx_request(request_queue_t * q);
struct block_device_operations mcdx_bdops =
{
owner: THIS_MODULE,
open: cdrom_open,
release: cdrom_release,
ioctl: cdrom_ioctl,
check_media_change: cdrom_media_changed,
};
/* Indirect exported functions. These functions are exported by their
addresses, such as mcdx_open and mcdx_close in the
structure mcdx_dops. */
/* ??? exported by the mcdx_sigaction struct */
static void mcdx_intr(int, void *, struct pt_regs *);
/* exported by file_ops */
static int mcdx_open(struct cdrom_device_info *cdi, int purpose);
static void mcdx_close(struct cdrom_device_info *cdi);
static int mcdx_media_changed(struct cdrom_device_info *cdi, int disc_nr);
static int mcdx_tray_move(struct cdrom_device_info *cdi, int position);
static int mcdx_lockdoor(struct cdrom_device_info *cdi, int lock);
static int mcdx_audio_ioctl(struct cdrom_device_info *cdi,
unsigned int cmd, void *arg);
/* misc internal support functions */
static void log2msf(unsigned int, struct cdrom_msf0 *);
static unsigned int msf2log(const struct cdrom_msf0 *);
static unsigned int uint2bcd(unsigned int);
static unsigned int bcd2uint(unsigned char);
static char *port(int *);
static int irq(int *);
static void mcdx_delay(struct s_drive_stuff *, long jifs);
static int mcdx_transfer(struct s_drive_stuff *, char *buf, int sector,
int nr_sectors);
static int mcdx_xfer(struct s_drive_stuff *, char *buf, int sector,
int nr_sectors);
static int mcdx_config(struct s_drive_stuff *, int);
static int mcdx_requestversion(struct s_drive_stuff *, struct s_version *,
int);
static int mcdx_stop(struct s_drive_stuff *, int);
static int mcdx_hold(struct s_drive_stuff *, int);
static int mcdx_reset(struct s_drive_stuff *, enum resetmodes, int);
static int mcdx_setdrivemode(struct s_drive_stuff *, enum drivemodes, int);
static int mcdx_setdatamode(struct s_drive_stuff *, enum datamodes, int);
static int mcdx_requestsubqcode(struct s_drive_stuff *,
struct s_subqcode *, int);
static int mcdx_requestmultidiskinfo(struct s_drive_stuff *,
struct s_multi *, int);
static int mcdx_requesttocdata(struct s_drive_stuff *, struct s_diskinfo *,
int);
static int mcdx_getstatus(struct s_drive_stuff *, int);
static int mcdx_getval(struct s_drive_stuff *, int to, int delay, char *);
static int mcdx_talk(struct s_drive_stuff *,
const unsigned char *cmd, size_t,
void *buffer, size_t size, unsigned int timeout, int);
static int mcdx_readtoc(struct s_drive_stuff *);
static int mcdx_playtrk(struct s_drive_stuff *, const struct cdrom_ti *);
static int mcdx_playmsf(struct s_drive_stuff *, const struct cdrom_msf *);
static int mcdx_setattentuator(struct s_drive_stuff *,
struct cdrom_volctrl *, int);
/* static variables ************************************************/
static int mcdx_blocksizes[MCDX_NDRIVES];
static int mcdx_drive_map[][2] = MCDX_DRIVEMAP;
static struct s_drive_stuff *mcdx_stuffp[MCDX_NDRIVES];
static struct s_drive_stuff *mcdx_irq_map[16] = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
MODULE_PARM(mcdx, "1-4i");
static struct cdrom_device_ops mcdx_dops = {
open:mcdx_open,
release:mcdx_close,
media_changed:mcdx_media_changed,
tray_move:mcdx_tray_move,
lock_door:mcdx_lockdoor,
audio_ioctl:mcdx_audio_ioctl,
capability:CDC_OPEN_TRAY | CDC_LOCK | CDC_MEDIA_CHANGED |
CDC_PLAY_AUDIO | CDC_DRIVE_STATUS,
};
static struct cdrom_device_info mcdx_info = {
ops:&mcdx_dops,
speed:2,
capacity:1,
name:"mcdx",
};
/* KERNEL INTERFACE FUNCTIONS **************************************/
static int mcdx_audio_ioctl(struct cdrom_device_info *cdi,
unsigned int cmd, void *arg)
{
struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(cdi->dev)];
if (!stuffp->present)
return -ENXIO;
if (stuffp->xxx) {
if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) {
stuffp->lastsector = -1;
} else {
stuffp->lastsector = (CD_FRAMESIZE / 512)
* msf2log(&stuffp->di.msf_leadout) - 1;
}
if (stuffp->toc) {
kfree(stuffp->toc);
stuffp->toc = NULL;
if (-1 == mcdx_readtoc(stuffp))
return -1;
}
stuffp->xxx = 0;
}
switch (cmd) {
case CDROMSTART:{
xtrace(IOCTL, "ioctl() START\n");
/* Spin up the drive. Don't think we can do this.
* For now, ignore it.
*/
return 0;
}
case CDROMSTOP:{
xtrace(IOCTL, "ioctl() STOP\n");
stuffp->audiostatus = CDROM_AUDIO_INVALID;
if (-1 == mcdx_stop(stuffp, 1))
return -EIO;
return 0;
}
case CDROMPLAYTRKIND:{
struct cdrom_ti *ti = (struct cdrom_ti *) arg;
xtrace(IOCTL, "ioctl() PLAYTRKIND\n");
if ((ti->cdti_trk0 < stuffp->di.n_first)
|| (ti->cdti_trk0 > stuffp->di.n_last)
|| (ti->cdti_trk1 < stuffp->di.n_first))
return -EINVAL;
if (ti->cdti_trk1 > stuffp->di.n_last)
ti->cdti_trk1 = stuffp->di.n_last;
xtrace(PLAYTRK, "ioctl() track %d to %d\n",
ti->cdti_trk0, ti->cdti_trk1);
return mcdx_playtrk(stuffp, ti);
}
case CDROMPLAYMSF:{
struct cdrom_msf *msf = (struct cdrom_msf *) arg;
xtrace(IOCTL, "ioctl() PLAYMSF\n");
if ((stuffp->audiostatus == CDROM_AUDIO_PLAY)
&& (-1 == mcdx_hold(stuffp, 1)))
return -EIO;
msf->cdmsf_min0 = uint2bcd(msf->cdmsf_min0);
msf->cdmsf_sec0 = uint2bcd(msf->cdmsf_sec0);
msf->cdmsf_frame0 = uint2bcd(msf->cdmsf_frame0);
msf->cdmsf_min1 = uint2bcd(msf->cdmsf_min1);
msf->cdmsf_sec1 = uint2bcd(msf->cdmsf_sec1);
msf->cdmsf_frame1 = uint2bcd(msf->cdmsf_frame1);
stuffp->stop.dt.minute = msf->cdmsf_min1;
stuffp->stop.dt.second = msf->cdmsf_sec1;
stuffp->stop.dt.frame = msf->cdmsf_frame1;
return mcdx_playmsf(stuffp, msf);
}
case CDROMRESUME:{
xtrace(IOCTL, "ioctl() RESUME\n");
return mcdx_playtrk(stuffp, NULL);
}
case CDROMREADTOCENTRY:{
struct cdrom_tocentry *entry =
(struct cdrom_tocentry *) arg;
struct s_subqcode *tp = NULL;
xtrace(IOCTL, "ioctl() READTOCENTRY\n");
if (-1 == mcdx_readtoc(stuffp))
return -1;
if (entry->cdte_track == CDROM_LEADOUT)
tp = &stuffp->toc[stuffp->di.n_last -
stuffp->di.n_first + 1];
else if (entry->cdte_track > stuffp->di.n_last
|| entry->cdte_track < stuffp->di.n_first)
return -EINVAL;
else
tp = &stuffp->toc[entry->cdte_track -
stuffp->di.n_first];
if (NULL == tp)
return -EIO;
entry->cdte_adr = tp->control;
entry->cdte_ctrl = tp->control >> 4;
/* Always return stuff in MSF, and let the Uniform cdrom driver
worry about what the user actually wants */
entry->cdte_addr.msf.minute =
bcd2uint(tp->dt.minute);
entry->cdte_addr.msf.second =
bcd2uint(tp->dt.second);
entry->cdte_addr.msf.frame =
bcd2uint(tp->dt.frame);
return 0;
}
case CDROMSUBCHNL:{
struct cdrom_subchnl *sub =
(struct cdrom_subchnl *) arg;
struct s_subqcode q;
xtrace(IOCTL, "ioctl() SUBCHNL\n");
if (-1 == mcdx_requestsubqcode(stuffp, &q, 2))
return -EIO;
xtrace(SUBCHNL, "audiostatus: %x\n",
stuffp->audiostatus);
sub->cdsc_audiostatus = stuffp->audiostatus;
sub->cdsc_adr = q.control;
sub->cdsc_ctrl = q.control >> 4;
sub->cdsc_trk = bcd2uint(q.tno);
sub->cdsc_ind = bcd2uint(q.index);
xtrace(SUBCHNL, "trk %d, ind %d\n",
sub->cdsc_trk, sub->cdsc_ind);
/* Always return stuff in MSF, and let the Uniform cdrom driver
worry about what the user actually wants */
sub->cdsc_absaddr.msf.minute =
bcd2uint(q.dt.minute);
sub->cdsc_absaddr.msf.second =
bcd2uint(q.dt.second);
sub->cdsc_absaddr.msf.frame = bcd2uint(q.dt.frame);
sub->cdsc_reladdr.msf.minute =
bcd2uint(q.tt.minute);
sub->cdsc_reladdr.msf.second =
bcd2uint(q.tt.second);
sub->cdsc_reladdr.msf.frame = bcd2uint(q.tt.frame);
xtrace(SUBCHNL,
"msf: abs %02d:%02d:%02d, rel %02d:%02d:%02d\n",
sub->cdsc_absaddr.msf.minute,
sub->cdsc_absaddr.msf.second,
sub->cdsc_absaddr.msf.frame,
sub->cdsc_reladdr.msf.minute,
sub->cdsc_reladdr.msf.second,
sub->cdsc_reladdr.msf.frame);
return 0;
}
case CDROMREADTOCHDR:{
struct cdrom_tochdr *toc =
(struct cdrom_tochdr *) arg;
xtrace(IOCTL, "ioctl() READTOCHDR\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -