📄 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? * */#if RCSstatic 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/malloc.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>/* for compatible parameter passing with "insmod" */#define mcdx_drive_map mcdx#include "mcdx.h"#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 */ struct wait_queue *busyq; struct wait_queue *lockq; struct wait_queue *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(void);/* already declared in init/main */void mcdx_setup(char *, int *);/* 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 = { mcdx_open, /* open */ mcdx_close, /* release */ NULL, /* drive status */ mcdx_media_changed, /* media changed */ mcdx_tray_move, /* tray move */ mcdx_lockdoor, /* lock door */ NULL, /* select speed */ NULL, /* select disc */ NULL, /* get last session */ NULL, /* get universal product code */ NULL, /* hard reset */ mcdx_audio_ioctl, /* audio ioctl */ NULL, /* device-specific ioctl */ CDC_OPEN_TRAY | CDC_LOCK | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_DRIVE_STATUS, /* capability */ 0, /* number of minor devices */};static struct cdrom_device_info mcdx_info = { &mcdx_dops, /* device operations */ NULL, /* link */ NULL, /* handle */ 0, /* dev */ 0, /* mask */ 2, /* maximum speed */ 1, /* number of discs */ 0, /* options, not owned */ 0, /* mc_flags, not owned */ 0, /* use count, not owned */ "mcdx", /* name of the device type */};/* 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,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -