⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mcdx.c

📁 cdrom device drive for linux.
💻 C
📖 第 1 页 / 共 4 页
字号:
/*
 * 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 + -