📄 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 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/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 + -