linux-old/drivers/cdrom/mcdx.c
<<
>>
Prefs
   1/*
   2 * The Mitsumi CDROM interface
   3 * Copyright (C) 1995 1996 Heiko Schlittermann <heiko@lotte.sax.de>
   4 * VERSION: 2.14(hs)
   5 *
   6 * ... anyway, I'm back again, thanks to Marcin, he adopted
   7 * large portions of my code (at least the parts containing
   8 * my main thoughts ...)
   9 *
  10 ****************** H E L P *********************************
  11 * If you ever plan to update your CD ROM drive and perhaps
  12 * want to sell or simply give away your Mitsumi FX-001[DS]
  13 * -- Please --
  14 * mail me (heiko@lotte.sax.de).  When my last drive goes
  15 * ballistic no more driver support will be available from me!
  16 *************************************************************
  17 *
  18 * This program is free software; you can redistribute it and/or modify
  19 * it under the terms of the GNU General Public License as published by
  20 * the Free Software Foundation; either version 2, or (at your option)
  21 * any later version.
  22 *
  23 * This program is distributed in the hope that it will be useful,
  24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  26 * GNU General Public License for more details.
  27 *
  28 * You should have received a copy of the GNU General Public License
  29 * along with this program; see the file COPYING.  If not, write to
  30 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  31 *
  32 * Thanks to
  33 *  The Linux Community at all and ...
  34 *  Martin Harriss (he wrote the first Mitsumi Driver)
  35 *  Eberhard Moenkeberg (he gave me much support and the initial kick)
  36 *  Bernd Huebner, Ruediger Helsch (Unifix-Software GmbH, they
  37 *      improved the original driver)
  38 *  Jon Tombs, Bjorn Ekwall (module support)
  39 *  Daniel v. Mosnenck (he sent me the Technical and Programming Reference)
  40 *  Gerd Knorr (he lent me his PhotoCD)
  41 *  Nils Faerber and Roger E. Wolff (extensively tested the LU portion)
  42 *  Andreas Kies (testing the mysterious hang-ups)
  43 *  Heiko Eissfeldt (VERIFY_READ/WRITE)
  44 *  Marcin Dalecki (improved performance, shortened code)
  45 *  ... somebody forgotten?
  46 *
  47 *  9 November 1999 -- Make kernel-parameter implementation work with 2.3.x 
  48 *                     Removed init_module & cleanup_module in favor of 
  49 *                     module_init & module_exit.
  50 *                     Torben Mathiasen <tmm@image.dk>
  51 */
  52
  53
  54#if RCS
  55static const char *mcdx_c_version
  56    = "$Id: mcdx.c,v 1.21 1997/01/26 07:12:59 davem Exp $";
  57#endif
  58
  59#include <linux/version.h>
  60#include <linux/module.h>
  61
  62#include <linux/errno.h>
  63#include <linux/sched.h>
  64#include <linux/fs.h>
  65#include <linux/kernel.h>
  66#include <linux/cdrom.h>
  67#include <linux/ioport.h>
  68#include <linux/mm.h>
  69#include <linux/slab.h>
  70#include <linux/init.h>
  71#include <asm/io.h>
  72#include <asm/uaccess.h>
  73
  74#include <linux/major.h>
  75#define MAJOR_NR MITSUMI_X_CDROM_MAJOR
  76#include <linux/blk.h>
  77#include <linux/devfs_fs_kernel.h>
  78
  79/* for compatible parameter passing with "insmod" */
  80#define mcdx_drive_map mcdx
  81#include "mcdx.h"
  82
  83#if BITS_PER_LONG != 32
  84#  error FIXME: this driver only works on 32-bit platforms
  85#endif
  86
  87#ifndef HZ
  88#error HZ not defined
  89#endif
  90
  91#define xwarn(fmt, args...) printk(KERN_WARNING MCDX " " fmt, ## args)
  92
  93#if !MCDX_QUIET
  94#define xinfo(fmt, args...) printk(KERN_INFO MCDX " " fmt, ## args)
  95#else
  96#define xinfo(fmt, args...) { ; }
  97#endif
  98
  99#if MCDX_DEBUG
 100#define xtrace(lvl, fmt, args...) \
 101                { if (lvl > 0) \
 102                        { printk(KERN_DEBUG MCDX ":: " fmt, ## args); } }
 103#define xdebug(fmt, args...) printk(KERN_DEBUG MCDX ":: " fmt, ## args)
 104#else
 105#define xtrace(lvl, fmt, args...) { ; }
 106#define xdebug(fmt, args...) { ; }
 107#endif
 108
 109/* CONSTANTS *******************************************************/
 110
 111/* Following are the number of sectors we _request_ from the drive
 112   every time an access outside the already requested range is done.
 113   The _direct_ size is the number of sectors we're allowed to skip
 114   directly (performing a read instead of requesting the new sector
 115   needed */
 116const int REQUEST_SIZE = 800;   /* should be less then 255 * 4 */
 117const int DIRECT_SIZE = 400;    /* should be less then REQUEST_SIZE */
 118
 119enum drivemodes { TOC, DATA, RAW, COOKED };
 120enum datamodes { MODE0, MODE1, MODE2 };
 121enum resetmodes { SOFT, HARD };
 122
 123const int SINGLE = 0x01;        /* single speed drive (FX001S, LU) */
 124const int DOUBLE = 0x02;        /* double speed drive (FX001D, ..? */
 125const int DOOR = 0x04;          /* door locking capability */
 126const int MULTI = 0x08;         /* multi session capability */
 127
 128const unsigned char READ1X = 0xc0;
 129const unsigned char READ2X = 0xc1;
 130
 131
 132/* DECLARATIONS ****************************************************/
 133struct s_subqcode {
 134        unsigned char control;
 135        unsigned char tno;
 136        unsigned char index;
 137        struct cdrom_msf0 tt;
 138        struct cdrom_msf0 dt;
 139};
 140
 141struct s_diskinfo {
 142        unsigned int n_first;
 143        unsigned int n_last;
 144        struct cdrom_msf0 msf_leadout;
 145        struct cdrom_msf0 msf_first;
 146};
 147
 148struct s_multi {
 149        unsigned char multi;
 150        struct cdrom_msf0 msf_last;
 151};
 152
 153struct s_version {
 154        unsigned char code;
 155        unsigned char ver;
 156};
 157
 158/* Per drive/controller stuff **************************************/
 159
 160struct s_drive_stuff {
 161        /* waitqueues */
 162        wait_queue_head_t busyq;
 163        wait_queue_head_t lockq;
 164        wait_queue_head_t sleepq;
 165
 166        /* flags */
 167        volatile int introk;    /* status of last irq operation */
 168        volatile int busy;      /* drive performs an operation */
 169        volatile int lock;      /* exclusive usage */
 170
 171        /* cd infos */
 172        struct s_diskinfo di;
 173        struct s_multi multi;
 174        struct s_subqcode *toc; /* first entry of the toc array */
 175        struct s_subqcode start;
 176        struct s_subqcode stop;
 177        int xa;                 /* 1 if xa disk */
 178        int audio;              /* 1 if audio disk */
 179        int audiostatus;
 180
 181        /* `buffer' control */
 182        volatile int valid;     /* pending, ..., values are valid */
 183        volatile int pending;   /* next sector to be read */
 184        volatile int low_border;        /* first sector not to be skipped direct */
 185        volatile int high_border;       /* first sector `out of area' */
 186#ifdef AK2
 187        volatile int int_err;
 188#endif                          /* AK2 */
 189
 190        /* adds and odds */
 191        void *wreg_data;        /* w data */
 192        void *wreg_reset;       /* w hardware reset */
 193        void *wreg_hcon;        /* w hardware conf */
 194        void *wreg_chn;         /* w channel */
 195        void *rreg_data;        /* r data */
 196        void *rreg_status;      /* r status */
 197
 198        int irq;                /* irq used by this drive */
 199        int minor;              /* minor number of this drive */
 200        int present;            /* drive present and its capabilities */
 201        unsigned char readcmd;  /* read cmd depends on single/double speed */
 202        unsigned char playcmd;  /* play should always be single speed */
 203        unsigned int xxx;       /* set if changed, reset while open */
 204        unsigned int yyy;       /* set if changed, reset by media_changed */
 205        int users;              /* keeps track of open/close */
 206        int lastsector;         /* last block accessible */
 207        int status;             /* last operation's error / status */
 208        int readerrs;           /* # of blocks read w/o error */
 209};
 210
 211
 212/* Prototypes ******************************************************/
 213
 214/*      The following prototypes are already declared elsewhere.  They are
 215        repeated here to show what's going on.  And to sense, if they're
 216        changed elsewhere. */
 217
 218/* declared in blk.h */
 219int mcdx_init(void);
 220void do_mcdx_request(request_queue_t * q);
 221
 222struct block_device_operations mcdx_bdops =
 223{
 224        owner:                  THIS_MODULE,
 225        open:                   cdrom_open,
 226        release:                cdrom_release,
 227        ioctl:                  cdrom_ioctl,
 228        check_media_change:     cdrom_media_changed,
 229};
 230
 231
 232/*      Indirect exported functions. These functions are exported by their
 233        addresses, such as mcdx_open and mcdx_close in the
 234        structure mcdx_dops. */
 235
 236/* ???  exported by the mcdx_sigaction struct */
 237static void mcdx_intr(int, void *, struct pt_regs *);
 238
 239/* exported by file_ops */
 240static int mcdx_open(struct cdrom_device_info *cdi, int purpose);
 241static void mcdx_close(struct cdrom_device_info *cdi);
 242static int mcdx_media_changed(struct cdrom_device_info *cdi, int disc_nr);
 243static int mcdx_tray_move(struct cdrom_device_info *cdi, int position);
 244static int mcdx_lockdoor(struct cdrom_device_info *cdi, int lock);
 245static int mcdx_audio_ioctl(struct cdrom_device_info *cdi,
 246                            unsigned int cmd, void *arg);
 247
 248/* misc internal support functions */
 249static void log2msf(unsigned int, struct cdrom_msf0 *);
 250static unsigned int msf2log(const struct cdrom_msf0 *);
 251static unsigned int uint2bcd(unsigned int);
 252static unsigned int bcd2uint(unsigned char);
 253static char *port(int *);
 254static int irq(int *);
 255static void mcdx_delay(struct s_drive_stuff *, long jifs);
 256static int mcdx_transfer(struct s_drive_stuff *, char *buf, int sector,
 257                         int nr_sectors);
 258static int mcdx_xfer(struct s_drive_stuff *, char *buf, int sector,
 259                     int nr_sectors);
 260
 261static int mcdx_config(struct s_drive_stuff *, int);
 262static int mcdx_requestversion(struct s_drive_stuff *, struct s_version *,
 263                               int);
 264static int mcdx_stop(struct s_drive_stuff *, int);
 265static int mcdx_hold(struct s_drive_stuff *, int);
 266static int mcdx_reset(struct s_drive_stuff *, enum resetmodes, int);
 267static int mcdx_setdrivemode(struct s_drive_stuff *, enum drivemodes, int);
 268static int mcdx_setdatamode(struct s_drive_stuff *, enum datamodes, int);
 269static int mcdx_requestsubqcode(struct s_drive_stuff *,
 270                                struct s_subqcode *, int);
 271static int mcdx_requestmultidiskinfo(struct s_drive_stuff *,
 272                                     struct s_multi *, int);
 273static int mcdx_requesttocdata(struct s_drive_stuff *, struct s_diskinfo *,
 274                               int);
 275static int mcdx_getstatus(struct s_drive_stuff *, int);
 276static int mcdx_getval(struct s_drive_stuff *, int to, int delay, char *);
 277static int mcdx_talk(struct s_drive_stuff *,
 278                     const unsigned char *cmd, size_t,
 279                     void *buffer, size_t size, unsigned int timeout, int);
 280static int mcdx_readtoc(struct s_drive_stuff *);
 281static int mcdx_playtrk(struct s_drive_stuff *, const struct cdrom_ti *);
 282static int mcdx_playmsf(struct s_drive_stuff *, const struct cdrom_msf *);
 283static int mcdx_setattentuator(struct s_drive_stuff *,
 284                               struct cdrom_volctrl *, int);
 285
 286/* static variables ************************************************/
 287
 288static int mcdx_blocksizes[MCDX_NDRIVES];
 289static int mcdx_drive_map[][2] = MCDX_DRIVEMAP;
 290static struct s_drive_stuff *mcdx_stuffp[MCDX_NDRIVES];
 291static struct s_drive_stuff *mcdx_irq_map[16] = { 0, 0, 0, 0, 0, 0, 0, 0,
 292        0, 0, 0, 0, 0, 0, 0, 0
 293};
 294MODULE_PARM(mcdx, "1-4i");
 295
 296static struct cdrom_device_ops mcdx_dops = {
 297        open:mcdx_open,
 298        release:mcdx_close,
 299        media_changed:mcdx_media_changed,
 300        tray_move:mcdx_tray_move,
 301        lock_door:mcdx_lockdoor,
 302        audio_ioctl:mcdx_audio_ioctl,
 303        capability:CDC_OPEN_TRAY | CDC_LOCK | CDC_MEDIA_CHANGED |
 304            CDC_PLAY_AUDIO | CDC_DRIVE_STATUS,
 305};
 306
 307static struct cdrom_device_info mcdx_info = {
 308        ops:&mcdx_dops,
 309        speed:2,
 310        capacity:1,
 311        name:"mcdx",
 312};
 313
 314
 315/* KERNEL INTERFACE FUNCTIONS **************************************/
 316
 317
 318static int mcdx_audio_ioctl(struct cdrom_device_info *cdi,
 319                            unsigned int cmd, void *arg)
 320{
 321        struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(cdi->dev)];
 322
 323        if (!stuffp->present)
 324                return -ENXIO;
 325
 326        if (stuffp->xxx) {
 327                if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) {
 328                        stuffp->lastsector = -1;
 329                } else {
 330                        stuffp->lastsector = (CD_FRAMESIZE / 512)
 331                            * msf2log(&stuffp->di.msf_leadout) - 1;
 332                }
 333
 334                if (stuffp->toc) {
 335                        kfree(stuffp->toc);
 336                        stuffp->toc = NULL;
 337                        if (-1 == mcdx_readtoc(stuffp))
 338                                return -1;
 339                }
 340
 341                stuffp->xxx = 0;
 342        }
 343
 344        switch (cmd) {
 345        case CDROMSTART:{
 346                        xtrace(IOCTL, "ioctl() START\n");
 347                        /* Spin up the drive.  Don't think we can do this.
 348                           * For now, ignore it.
 349                         */
 350                        return 0;
 351                }
 352
 353        case CDROMSTOP:{
 354                        xtrace(IOCTL, "ioctl() STOP\n");
 355                        stuffp->audiostatus = CDROM_AUDIO_INVALID;
 356                        if (-1 == mcdx_stop(stuffp, 1))
 357                                return -EIO;
 358                        return 0;
 359                }
 360
 361        case CDROMPLAYTRKIND:{
 362                        struct cdrom_ti *ti = (struct cdrom_ti *) arg;
 363
 364                        xtrace(IOCTL, "ioctl() PLAYTRKIND\n");
 365                        if ((ti->cdti_trk0 < stuffp->di.n_first)
 366                            || (ti->cdti_trk0 > stuffp->di.n_last)
 367                            || (ti->cdti_trk1 < stuffp->di.n_first))
 368                                return -EINVAL;
 369                        if (ti->cdti_trk1 > stuffp->di.n_last)
 370                                ti->cdti_trk1 = stuffp->di.n_last;
 371                        xtrace(PLAYTRK, "ioctl() track %d to %d\n",
 372                               ti->cdti_trk0, ti->cdti_trk1);
 373                        return mcdx_playtrk(stuffp, ti);
 374                }
 375
 376        case CDROMPLAYMSF:{
 377                        struct cdrom_msf *msf = (struct cdrom_msf *) arg;
 378
 379                        xtrace(IOCTL, "ioctl() PLAYMSF\n");
 380
 381                        if ((stuffp->audiostatus == CDROM_AUDIO_PLAY)
 382                            && (-1 == mcdx_hold(stuffp, 1)))
 383                                return -EIO;
 384
 385                        msf->cdmsf_min0 = uint2bcd(msf->cdmsf_min0);
 386                        msf->cdmsf_sec0 = uint2bcd(msf->cdmsf_sec0);
 387                        msf->cdmsf_frame0 = uint2bcd(msf->cdmsf_frame0);
 388
 389                        msf->cdmsf_min1 = uint2bcd(msf->cdmsf_min1);
 390                        msf->cdmsf_sec1 = uint2bcd(msf->cdmsf_sec1);
 391                        msf->cdmsf_frame1 = uint2bcd(msf->cdmsf_frame1);
 392
 393                        stuffp->stop.dt.minute = msf->cdmsf_min1;
 394                        stuffp->stop.dt.second = msf->cdmsf_sec1;
 395                        stuffp->stop.dt.frame = msf->cdmsf_frame1;
 396
 397                        return mcdx_playmsf(stuffp, msf);
 398                }
 399
 400        case CDROMRESUME:{
 401                        xtrace(IOCTL, "ioctl() RESUME\n");
 402                        return mcdx_playtrk(stuffp, NULL);
 403                }
 404
 405        case CDROMREADTOCENTRY:{
 406                        struct cdrom_tocentry *entry =
 407                            (struct cdrom_tocentry *) arg;
 408                        struct s_subqcode *tp = NULL;
 409                        xtrace(IOCTL, "ioctl() READTOCENTRY\n");
 410
 411                        if (-1 == mcdx_readtoc(stuffp))
 412                                return -1;
 413                        if (entry->cdte_track == CDROM_LEADOUT)
 414                                tp = &stuffp->toc[stuffp->di.n_last -
 415                                                  stuffp->di.n_first + 1];
 416                        else if (entry->cdte_track > stuffp->di.n_last
 417                                 || entry->cdte_track < stuffp->di.n_first)
 418                                return -EINVAL;
 419                        else
 420                                tp = &stuffp->toc[entry->cdte_track -
 421                                                  stuffp->di.n_first];
 422
 423                        if (NULL == tp)
 424                                return -EIO;
 425                        entry->cdte_adr = tp->control;
 426                        entry->cdte_ctrl = tp->control >> 4;
 427                        /* Always return stuff in MSF, and let the Uniform cdrom driver
 428                           worry about what the user actually wants */
 429                        entry->cdte_addr.msf.minute =
 430                            bcd2uint(tp->dt.minute);
 431                        entry->cdte_addr.msf.second =
 432                            bcd2uint(tp->dt.second);
 433                        entry->cdte_addr.msf.frame =
 434                            bcd2uint(tp->dt.frame);
 435                        return 0;
 436                }
 437
 438        case CDROMSUBCHNL:{
 439                        struct cdrom_subchnl *sub =
 440                            (struct cdrom_subchnl *) arg;
 441                        struct s_subqcode q;
 442
 443                        xtrace(IOCTL, "ioctl() SUBCHNL\n");
 444
 445                        if (-1 == mcdx_requestsubqcode(stuffp, &q, 2))
 446                                return -EIO;
 447
 448                        xtrace(SUBCHNL, "audiostatus: %x\n",
 449                               stuffp->audiostatus);
 450                        sub->cdsc_audiostatus = stuffp->audiostatus;
 451                        sub->cdsc_adr = q.control;
 452                        sub->cdsc_ctrl = q.control >> 4;
 453                        sub->cdsc_trk = bcd2uint(q.tno);
 454                        sub->cdsc_ind = bcd2uint(q.index);
 455
 456                        xtrace(SUBCHNL, "trk %d, ind %d\n",
 457                               sub->cdsc_trk, sub->cdsc_ind);
 458                        /* Always return stuff in MSF, and let the Uniform cdrom driver
 459                           worry about what the user actually wants */
 460                        sub->cdsc_absaddr.msf.minute =
 461                            bcd2uint(q.dt.minute);
 462                        sub->cdsc_absaddr.msf.second =
 463                            bcd2uint(q.dt.second);
 464                        sub->cdsc_absaddr.msf.frame = bcd2uint(q.dt.frame);
 465                        sub->cdsc_reladdr.msf.minute =
 466                            bcd2uint(q.tt.minute);
 467                        sub->cdsc_reladdr.msf.second =
 468                            bcd2uint(q.tt.second);
 469                        sub->cdsc_reladdr.msf.frame = bcd2uint(q.tt.frame);
 470                        xtrace(SUBCHNL,
 471                               "msf: abs %02d:%02d:%02d, rel %02d:%02d:%02d\n",
 472                               sub->cdsc_absaddr.msf.minute,
 473                               sub->cdsc_absaddr.msf.second,
 474                               sub->cdsc_absaddr.msf.frame,
 475                               sub->cdsc_reladdr.msf.minute,
 476                               sub->cdsc_reladdr.msf.second,
 477                               sub->cdsc_reladdr.msf.frame);
 478
 479                        return 0;
 480                }
 481
 482        case CDROMREADTOCHDR:{
 483                        struct cdrom_tochdr *toc =
 484                            (struct cdrom_tochdr *) arg;
 485
 486                        xtrace(IOCTL, "ioctl() READTOCHDR\n");
 487                        toc->cdth_trk0 = stuffp->di.n_first;
 488                        toc->cdth_trk1 = stuffp->di.n_last;
 489                        xtrace(TOCHDR,
 490                               "ioctl() track0 = %d, track1 = %d\n",
 491                               stuffp->di.n_first, stuffp->di.n_last);
 492                        return 0;
 493                }
 494
 495        case CDROMPAUSE:{
 496                        xtrace(IOCTL, "ioctl() PAUSE\n");
 497                        if (stuffp->audiostatus != CDROM_AUDIO_PLAY)
 498                                return -EINVAL;
 499                        if (-1 == mcdx_stop(stuffp, 1))
 500                                return -EIO;
 501                        stuffp->audiostatus = CDROM_AUDIO_PAUSED;
 502                        if (-1 ==
 503                            mcdx_requestsubqcode(stuffp, &stuffp->start,
 504                                                 1))
 505                                return -EIO;
 506                        return 0;
 507                }
 508
 509        case CDROMMULTISESSION:{
 510                        struct cdrom_multisession *ms =
 511                            (struct cdrom_multisession *) arg;
 512                        xtrace(IOCTL, "ioctl() MULTISESSION\n");
 513                        /* Always return stuff in LBA, and let the Uniform cdrom driver
 514                           worry about what the user actually wants */
 515                        ms->addr.lba = msf2log(&stuffp->multi.msf_last);
 516                        ms->xa_flag = !!stuffp->multi.multi;
 517                        xtrace(MS,
 518                               "ioctl() (%d, 0x%08x [%02x:%02x.%02x])\n",
 519                               ms->xa_flag, ms->addr.lba,
 520                               stuffp->multi.msf_last.minute,
 521                               stuffp->multi.msf_last.second,
 522                               stuffp->multi.msf_last.frame);
 523
 524                        return 0;
 525                }
 526
 527        case CDROMEJECT:{
 528                        xtrace(IOCTL, "ioctl() EJECT\n");
 529                        if (stuffp->users > 1)
 530                                return -EBUSY;
 531                        return (mcdx_tray_move(cdi, 1));
 532                }
 533
 534        case CDROMCLOSETRAY:{
 535                        xtrace(IOCTL, "ioctl() CDROMCLOSETRAY\n");
 536                        return (mcdx_tray_move(cdi, 0));
 537                }
 538
 539        case CDROMVOLCTRL:{
 540                        struct cdrom_volctrl *volctrl =
 541                            (struct cdrom_volctrl *) arg;
 542                        xtrace(IOCTL, "ioctl() VOLCTRL\n");
 543
 544#if 0                           /* not tested! */
 545                        /* adjust for the weirdness of workman (md) */
 546                        /* can't test it (hs) */
 547                        volctrl.channel2 = volctrl.channel1;
 548                        volctrl.channel1 = volctrl.channel3 = 0x00;
 549#endif
 550                        return mcdx_setattentuator(stuffp, volctrl, 2);
 551                }
 552
 553        default:
 554                return -EINVAL;
 555        }
 556}
 557
 558void do_mcdx_request(request_queue_t * q)
 559{
 560        int dev;
 561        struct s_drive_stuff *stuffp;
 562
 563      again:
 564
 565        if (QUEUE_EMPTY) {
 566                xtrace(REQUEST, "end_request(0): CURRENT == NULL\n");
 567                return;
 568        }
 569
 570        if (CURRENT->rq_status == RQ_INACTIVE) {
 571                xtrace(REQUEST,
 572                       "end_request(0): rq_status == RQ_INACTIVE\n");
 573                return;
 574        }
 575
 576        INIT_REQUEST;
 577
 578        dev = MINOR(CURRENT->rq_dev);
 579        stuffp = mcdx_stuffp[dev];
 580
 581        if ((dev < 0)
 582            || (dev >= MCDX_NDRIVES)
 583            || !stuffp || (!stuffp->present)) {
 584                xwarn("do_request(): bad device: %s\n",
 585                      kdevname(CURRENT->rq_dev));
 586                xtrace(REQUEST, "end_request(0): bad device\n");
 587                end_request(0);
 588                return;
 589        }
 590
 591        if (stuffp->audio) {
 592                xwarn("do_request() attempt to read from audio cd\n");
 593                xtrace(REQUEST, "end_request(0): read from audio\n");
 594                end_request(0);
 595                return;
 596        }
 597
 598        xtrace(REQUEST, "do_request() (%lu + %lu)\n",
 599               CURRENT->sector, CURRENT->nr_sectors);
 600
 601        switch (CURRENT->cmd) {
 602        case WRITE:
 603                xwarn("do_request(): attempt to write to cd!!\n");
 604                xtrace(REQUEST, "end_request(0): write\n");
 605                end_request(0);
 606                return;
 607
 608        case READ:
 609                stuffp->status = 0;
 610                while (CURRENT->nr_sectors) {
 611                        int i;
 612
 613                        i = mcdx_transfer(stuffp,
 614                                          CURRENT->buffer,
 615                                          CURRENT->sector,
 616                                          CURRENT->nr_sectors);
 617
 618                        if (i == -1) {
 619                                end_request(0);
 620                                goto again;
 621                        }
 622                        CURRENT->sector += i;
 623                        CURRENT->nr_sectors -= i;
 624                        CURRENT->buffer += (i * 512);
 625                }
 626                end_request(1);
 627                goto again;
 628
 629                xtrace(REQUEST, "end_request(1)\n");
 630                end_request(1);
 631                break;
 632
 633        default:
 634                panic(MCDX "do_request: unknown command.\n");
 635                break;
 636        }
 637
 638        goto again;
 639}
 640
 641static int mcdx_open(struct cdrom_device_info *cdi, int purpose)
 642{
 643        struct s_drive_stuff *stuffp;
 644        xtrace(OPENCLOSE, "open()\n");
 645        stuffp = mcdx_stuffp[MINOR(cdi->dev)];
 646        if (!stuffp->present)
 647                return -ENXIO;
 648
 649        /* Make the modules looking used ... (thanx bjorn).
 650         * But we shouldn't forget to decrement the module counter
 651         * on error return */
 652
 653        /* this is only done to test if the drive talks with us */
 654        if (-1 == mcdx_getstatus(stuffp, 1))
 655                return -EIO;
 656
 657        if (stuffp->xxx) {
 658
 659                xtrace(OPENCLOSE, "open() media changed\n");
 660                stuffp->audiostatus = CDROM_AUDIO_INVALID;
 661                stuffp->readcmd = 0;
 662                xtrace(OPENCLOSE, "open() Request multisession info\n");
 663                if (-1 ==
 664                    mcdx_requestmultidiskinfo(stuffp, &stuffp->multi, 6))
 665                        xinfo("No multidiskinfo\n");
 666        } else {
 667                /* multisession ? */
 668                if (!stuffp->multi.multi)
 669                        stuffp->multi.msf_last.second = 2;
 670
 671                xtrace(OPENCLOSE, "open() MS: %d, last @ %02x:%02x.%02x\n",
 672                       stuffp->multi.multi,
 673                       stuffp->multi.msf_last.minute,
 674                       stuffp->multi.msf_last.second,
 675                       stuffp->multi.msf_last.frame);
 676
 677                {;
 678                }               /* got multisession information */
 679                /* request the disks table of contents (aka diskinfo) */
 680                if (-1 == mcdx_requesttocdata(stuffp, &stuffp->di, 1)) {
 681
 682                        stuffp->lastsector = -1;
 683
 684                } else {
 685
 686                        stuffp->lastsector = (CD_FRAMESIZE / 512)
 687                            * msf2log(&stuffp->di.msf_leadout) - 1;
 688
 689                        xtrace(OPENCLOSE,
 690                               "open() start %d (%02x:%02x.%02x) %d\n",
 691                               stuffp->di.n_first,
 692                               stuffp->di.msf_first.minute,
 693                               stuffp->di.msf_first.second,
 694                               stuffp->di.msf_first.frame,
 695                               msf2log(&stuffp->di.msf_first));
 696                        xtrace(OPENCLOSE,
 697                               "open() last %d (%02x:%02x.%02x) %d\n",
 698                               stuffp->di.n_last,
 699                               stuffp->di.msf_leadout.minute,
 700                               stuffp->di.msf_leadout.second,
 701                               stuffp->di.msf_leadout.frame,
 702                               msf2log(&stuffp->di.msf_leadout));
 703                }
 704
 705                if (stuffp->toc) {
 706                        xtrace(MALLOC, "open() free old toc @ %p\n",
 707                               stuffp->toc);
 708                        kfree(stuffp->toc);
 709
 710                        stuffp->toc = NULL;
 711                }
 712
 713                xtrace(OPENCLOSE, "open() init irq generation\n");
 714                if (-1 == mcdx_config(stuffp, 1))
 715                        return -EIO;
 716#if FALLBACK
 717                /* Set the read speed */
 718                xwarn("AAA %x AAA\n", stuffp->readcmd);
 719                if (stuffp->readerrs)
 720                        stuffp->readcmd = READ1X;
 721                else
 722                        stuffp->readcmd =
 723                            stuffp->present | SINGLE ? READ1X : READ2X;
 724                xwarn("XXX %x XXX\n", stuffp->readcmd);
 725#else
 726                stuffp->readcmd =
 727                    stuffp->present | SINGLE ? READ1X : READ2X;
 728#endif
 729
 730                /* try to get the first sector, iff any ... */
 731                if (stuffp->lastsector >= 0) {
 732                        char buf[512];
 733                        int ans;
 734                        int tries;
 735
 736                        stuffp->xa = 0;
 737                        stuffp->audio = 0;
 738
 739                        for (tries = 6; tries; tries--) {
 740
 741                                stuffp->introk = 1;
 742
 743                                xtrace(OPENCLOSE, "open() try as %s\n",
 744                                       stuffp->xa ? "XA" : "normal");
 745                                /* set data mode */
 746                                if (-1 == (ans = mcdx_setdatamode(stuffp,
 747                                                                  stuffp->
 748                                                                  xa ?
 749                                                                  MODE2 :
 750                                                                  MODE1,
 751                                                                  1))) {
 752                                        /* return -EIO; */
 753                                        stuffp->xa = 0;
 754                                        break;
 755                                }
 756
 757                                if ((stuffp->audio = e_audio(ans)))
 758                                        break;
 759
 760                                while (0 ==
 761                                       (ans =
 762                                        mcdx_transfer(stuffp, buf, 0, 1)));
 763
 764                                if (ans == 1)
 765                                        break;
 766                                stuffp->xa = !stuffp->xa;
 767                        }
 768                }
 769                /* xa disks will be read in raw mode, others not */
 770                if (-1 == mcdx_setdrivemode(stuffp,
 771                                            stuffp->xa ? RAW : COOKED,
 772                                            1))
 773                        return -EIO;
 774                if (stuffp->audio) {
 775                        xinfo("open() audio disk found\n");
 776                } else if (stuffp->lastsector >= 0) {
 777                        xinfo("open() %s%s disk found\n",
 778                              stuffp->xa ? "XA / " : "",
 779                              stuffp->multi.
 780                              multi ? "Multi Session" : "Single Session");
 781                }
 782        }
 783        stuffp->xxx = 0;
 784        stuffp->users++;
 785        return 0;
 786}
 787
 788static void mcdx_close(struct cdrom_device_info *cdi)
 789{
 790        struct s_drive_stuff *stuffp;
 791
 792        xtrace(OPENCLOSE, "close()\n");
 793
 794        stuffp = mcdx_stuffp[MINOR(cdi->dev)];
 795
 796        --stuffp->users;
 797}
 798
 799static int mcdx_media_changed(struct cdrom_device_info *cdi, int disc_nr)
 800/*      Return: 1 if media changed since last call to this function
 801                        0 otherwise */
 802{
 803        struct s_drive_stuff *stuffp;
 804
 805        xinfo("mcdx_media_changed called for device %s\n",
 806              kdevname(cdi->dev));
 807
 808        stuffp = mcdx_stuffp[MINOR(cdi->dev)];
 809        mcdx_getstatus(stuffp, 1);
 810
 811        if (stuffp->yyy == 0)
 812                return 0;
 813
 814        stuffp->yyy = 0;
 815        return 1;
 816}
 817
 818#ifndef MODULE
 819static int __init mcdx_setup(char *str)
 820{
 821        int pi[4];
 822        (void) get_options(str, ARRAY_SIZE(pi), pi);
 823
 824        if (pi[0] > 0)
 825                mcdx_drive_map[0][0] = pi[1];
 826        if (pi[0] > 1)
 827                mcdx_drive_map[0][1] = pi[2];
 828        return 1;
 829}
 830
 831__setup("mcdx=", mcdx_setup);
 832
 833#endif
 834
 835/* DIRTY PART ******************************************************/
 836
 837static void mcdx_delay(struct s_drive_stuff *stuff, long jifs)
 838/* This routine is used for sleeping.
 839 * A jifs value <0 means NO sleeping,
 840 *              =0 means minimal sleeping (let the kernel
 841 *                 run for other processes)
 842 *              >0 means at least sleep for that amount.
 843 *      May be we could use a simple count loop w/ jumps to itself, but
 844 *      I wanna make this independent of cpu speed. [1 jiffy is 1/HZ] sec */
 845{
 846        if (jifs < 0)
 847                return;
 848
 849        xtrace(SLEEP, "*** delay: sleepq\n");
 850        interruptible_sleep_on_timeout(&stuff->sleepq, jifs);
 851        xtrace(SLEEP, "delay awoken\n");
 852        if (signal_pending(current)) {
 853                xtrace(SLEEP, "got signal\n");
 854        }
 855}
 856
 857static void mcdx_intr(int irq, void *dev_id, struct pt_regs *regs)
 858{
 859        struct s_drive_stuff *stuffp;
 860        unsigned char b;
 861
 862        stuffp = mcdx_irq_map[irq];
 863
 864        if (stuffp == NULL) {
 865                xwarn("mcdx: no device for intr %d\n", irq);
 866                return;
 867        }
 868#ifdef AK2
 869        if (!stuffp->busy && stuffp->pending)
 870                stuffp->int_err = 1;
 871
 872#endif                          /* AK2 */
 873        /* get the interrupt status */
 874        b = inb((unsigned int) stuffp->rreg_status);
 875        stuffp->introk = ~b & MCDX_RBIT_DTEN;
 876
 877        /* NOTE: We only should get interrupts if the data we
 878         * requested are ready to transfer.
 879         * But the drive seems to generate ``asynchronous'' interrupts
 880         * on several error conditions too.  (Despite the err int enable
 881         * setting during initialisation) */
 882
 883        /* if not ok, read the next byte as the drives status */
 884        if (!stuffp->introk) {
 885                xtrace(IRQ, "intr() irq %d hw status 0x%02x\n", irq, b);
 886                if (~b & MCDX_RBIT_STEN) {
 887                        xinfo("intr() irq %d    status 0x%02x\n",
 888                              irq, inb((unsigned int) stuffp->rreg_data));
 889                } else {
 890                        xinfo("intr() irq %d ambiguous hw status\n", irq);
 891                }
 892        } else {
 893                xtrace(IRQ, "irq() irq %d ok, status %02x\n", irq, b);
 894        }
 895
 896        stuffp->busy = 0;
 897        wake_up_interruptible(&stuffp->busyq);
 898}
 899
 900
 901static int mcdx_talk(struct s_drive_stuff *stuffp,
 902          const unsigned char *cmd, size_t cmdlen,
 903          void *buffer, size_t size, unsigned int timeout, int tries)
 904/* Send a command to the drive, wait for the result.
 905 * returns -1 on timeout, drive status otherwise
 906 * If buffer is not zero, the result (length size) is stored there.
 907 * If buffer is zero the size should be the number of bytes to read
 908 * from the drive.  These bytes are discarded.
 909 */
 910{
 911        int st;
 912        char c;
 913        int discard;
 914
 915        /* Somebody wants the data read? */
 916        if ((discard = (buffer == NULL)))
 917                buffer = &c;
 918
 919        while (stuffp->lock) {
 920                xtrace(SLEEP, "*** talk: lockq\n");
 921                interruptible_sleep_on(&stuffp->lockq);
 922                xtrace(SLEEP, "talk: awoken\n");
 923        }
 924
 925        stuffp->lock = 1;
 926
 927        /* An operation other then reading data destroys the
 928           * data already requested and remembered in stuffp->request, ... */
 929        stuffp->valid = 0;
 930
 931#if MCDX_DEBUG & TALK
 932        {
 933                unsigned char i;
 934                xtrace(TALK,
 935                       "talk() %d / %d tries, res.size %d, command 0x%02x",
 936                       tries, timeout, size, (unsigned char) cmd[0]);
 937                for (i = 1; i < cmdlen; i++)
 938                        xtrace(TALK, " 0x%02x", cmd[i]);
 939                xtrace(TALK, "\n");
 940        }
 941#endif
 942
 943        /*  give up if all tries are done (bad) or if the status
 944         *  st != -1 (good) */
 945        for (st = -1; st == -1 && tries; tries--) {
 946
 947                char *bp = (char *) buffer;
 948                size_t sz = size;
 949
 950                outsb((unsigned int) stuffp->wreg_data, cmd, cmdlen);
 951                xtrace(TALK, "talk() command sent\n");
 952
 953                /* get the status byte */
 954                if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) {
 955                        xinfo("talk() %02x timed out (status), %d tr%s left\n",
 956                             cmd[0], tries - 1, tries == 2 ? "y" : "ies");
 957                        continue;
 958                }
 959                st = *bp;
 960                sz--;
 961                if (!discard)
 962                        bp++;
 963
 964                xtrace(TALK, "talk() got status 0x%02x\n", st);
 965
 966                /* command error? */
 967                if (e_cmderr(st)) {
 968                        xwarn("command error cmd = %02x %s \n",
 969                              cmd[0], cmdlen > 1 ? "..." : "");
 970                        st = -1;
 971                        continue;
 972                }
 973
 974                /* audio status? */
 975                if (stuffp->audiostatus == CDROM_AUDIO_INVALID)
 976                        stuffp->audiostatus =
 977                            e_audiobusy(st) ? CDROM_AUDIO_PLAY :
 978                            CDROM_AUDIO_NO_STATUS;
 979                else if (stuffp->audiostatus == CDROM_AUDIO_PLAY
 980                         && e_audiobusy(st) == 0)
 981                        stuffp->audiostatus = CDROM_AUDIO_COMPLETED;
 982
 983                /* media change? */
 984                if (e_changed(st)) {
 985                        xinfo("talk() media changed\n");
 986                        stuffp->xxx = stuffp->yyy = 1;
 987                }
 988
 989                /* now actually get the data */
 990                while (sz--) {
 991                        if (-1 == mcdx_getval(stuffp, timeout, 0, bp)) {
 992                                xinfo("talk() %02x timed out (data), %d tr%s left\n",
 993                                     cmd[0], tries - 1,
 994                                     tries == 2 ? "y" : "ies");
 995                                st = -1;
 996                                break;
 997                        }
 998                        if (!discard)
 999                                bp++;
1000                        xtrace(TALK, "talk() got 0x%02x\n", *(bp - 1));
1001                }
1002        }
1003
1004#if !MCDX_QUIET
1005        if (!tries && st == -1)
1006                xinfo("talk() giving up\n");
1007#endif
1008
1009        stuffp->lock = 0;
1010        wake_up_interruptible(&stuffp->lockq);
1011
1012        xtrace(TALK, "talk() done with 0x%02x\n", st);
1013        return st;
1014}
1015
1016/* MODULE STUFF ***********************************************************/
1017
1018EXPORT_NO_SYMBOLS;
1019
1020int __mcdx_init(void)
1021{
1022        int i;
1023        int drives = 0;
1024
1025        mcdx_init();
1026        for (i = 0; i < MCDX_NDRIVES; i++) {
1027                if (mcdx_stuffp[i]) {
1028                        xtrace(INIT, "init_module() drive %d stuff @ %p\n",
1029                               i, mcdx_stuffp[i]);
1030                        drives++;
1031                }
1032        }
1033
1034        if (!drives)
1035                return -EIO;
1036
1037        return 0;
1038}
1039
1040void __exit mcdx_exit(void)
1041{
1042        int i;
1043
1044        xinfo("cleanup_module called\n");
1045
1046        for (i = 0; i < MCDX_NDRIVES; i++) {
1047                struct s_drive_stuff *stuffp;
1048                if (unregister_cdrom(&mcdx_info)) {
1049                        printk(KERN_WARNING "Can't unregister cdrom mcdx\n");
1050                        return;
1051                }
1052                stuffp = mcdx_stuffp[i];
1053                if (!stuffp)
1054                        continue;
1055                release_region((unsigned long) stuffp->wreg_data,
1056                               MCDX_IO_SIZE);
1057                free_irq(stuffp->irq, NULL);
1058                if (stuffp->toc) {
1059                        xtrace(MALLOC, "cleanup_module() free toc @ %p\n",
1060                               stuffp->toc);
1061                        kfree(stuffp->toc);
1062                }
1063                xtrace(MALLOC, "cleanup_module() free stuffp @ %p\n",
1064                       stuffp);
1065                mcdx_stuffp[i] = NULL;
1066                kfree(stuffp);
1067        }
1068
1069        if (devfs_unregister_blkdev(MAJOR_NR, "mcdx") != 0) {
1070                xwarn("cleanup() unregister_blkdev() failed\n");
1071        }
1072        blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
1073#if !MCDX_QUIET
1074        else
1075        xinfo("cleanup() succeeded\n");
1076#endif
1077}
1078
1079#ifdef MODULE
1080module_init(__mcdx_init);
1081#endif
1082module_exit(mcdx_exit);
1083
1084
1085/* Support functions ************************************************/
1086
1087int __init mcdx_init_drive(int drive)
1088{
1089        struct s_version version;
1090        struct s_drive_stuff *stuffp;
1091        int size = sizeof(*stuffp);
1092        char msg[80];
1093
1094        mcdx_blocksizes[drive] = 0;
1095
1096        xtrace(INIT, "init() try drive %d\n", drive);
1097
1098        xtrace(INIT, "kmalloc space for stuffpt's\n");
1099        xtrace(MALLOC, "init() malloc %d bytes\n", size);
1100        if (!(stuffp = kmalloc(size, GFP_KERNEL))) {
1101                xwarn("init() malloc failed\n");
1102                return 1;
1103        }
1104
1105        xtrace(INIT, "init() got %d bytes for drive stuff @ %p\n",
1106               sizeof(*stuffp), stuffp);
1107
1108        /* set default values */
1109        memset(stuffp, 0, sizeof(*stuffp));
1110
1111        stuffp->present = 0;    /* this should be 0 already */
1112        stuffp->toc = NULL;     /* this should be NULL already */
1113
1114        /* setup our irq and i/o addresses */
1115        stuffp->irq = irq(mcdx_drive_map[drive]);
1116        stuffp->wreg_data = stuffp->rreg_data =
1117            port(mcdx_drive_map[drive]);
1118        stuffp->wreg_reset = stuffp->rreg_status = stuffp->wreg_data + 1;
1119        stuffp->wreg_hcon = stuffp->wreg_reset + 1;
1120        stuffp->wreg_chn = stuffp->wreg_hcon + 1;
1121
1122        init_waitqueue_head(&stuffp->busyq);
1123        init_waitqueue_head(&stuffp->lockq);
1124        init_waitqueue_head(&stuffp->sleepq);
1125
1126        /* check if i/o addresses are available */
1127        if (check_region((unsigned int) stuffp->wreg_data, MCDX_IO_SIZE)) {
1128                xwarn("0x%3p,%d: Init failed. "
1129                      "I/O ports (0x%3p..0x%3p) already in use.\n",
1130                      stuffp->wreg_data, stuffp->irq,
1131                      stuffp->wreg_data,
1132                      stuffp->wreg_data + MCDX_IO_SIZE - 1);
1133                xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp);
1134                kfree(stuffp);
1135                xtrace(INIT, "init() continue at next drive\n");
1136                return 0;       /* next drive */
1137        }
1138
1139        xtrace(INIT, "init() i/o port is available at 0x%3p\n",
1140               stuffp->wreg_data);
1141        xtrace(INIT, "init() hardware reset\n");
1142        mcdx_reset(stuffp, HARD, 1);
1143
1144        xtrace(INIT, "init() get version\n");
1145        if (-1 == mcdx_requestversion(stuffp, &version, 4)) {
1146                /* failed, next drive */
1147                xwarn("%s=0x%3p,%d: Init failed. Can't get version.\n",
1148                      MCDX, stuffp->wreg_data, stuffp->irq);
1149                xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp);
1150                kfree(stuffp);
1151                xtrace(INIT, "init() continue at next drive\n");
1152                return 0;
1153        }
1154
1155        switch (version.code) {
1156        case 'D':
1157                stuffp->readcmd = READ2X;
1158                stuffp->present = DOUBLE | DOOR | MULTI;
1159                break;
1160        case 'F':
1161                stuffp->readcmd = READ1X;
1162                stuffp->present = SINGLE | DOOR | MULTI;
1163                break;
1164        case 'M':
1165                stuffp->readcmd = READ1X;
1166                stuffp->present = SINGLE;
1167                break;
1168        default:
1169                stuffp->present = 0;
1170                break;
1171        }
1172
1173        stuffp->playcmd = READ1X;
1174
1175        if (!stuffp->present) {
1176                xwarn("%s=0x%3p,%d: Init failed. No Mitsumi CD-ROM?.\n",
1177                      MCDX, stuffp->wreg_data, stuffp->irq);
1178                kfree(stuffp);
1179                return 0;       /* next drive */
1180        }
1181
1182        xtrace(INIT, "init() register blkdev\n");
1183        if (devfs_register_blkdev(MAJOR_NR, "mcdx", &mcdx_bdops) != 0) {
1184                xwarn("%s=0x%3p,%d: Init failed. Can't get major %d.\n",
1185                      MCDX, stuffp->wreg_data, stuffp->irq, MAJOR_NR);
1186                kfree(stuffp);
1187                return 1;
1188        }
1189
1190        blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
1191        read_ahead[MAJOR_NR] = READ_AHEAD;
1192        blksize_size[MAJOR_NR] = mcdx_blocksizes;
1193
1194        xtrace(INIT, "init() subscribe irq and i/o\n");
1195        mcdx_irq_map[stuffp->irq] = stuffp;
1196        if (request_irq(stuffp->irq, mcdx_intr, SA_INTERRUPT, "mcdx", NULL)) {
1197                xwarn("%s=0x%3p,%d: Init failed. Can't get irq (%d).\n",
1198                      MCDX, stuffp->wreg_data, stuffp->irq, stuffp->irq);
1199                stuffp->irq = 0;
1200                blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
1201                kfree(stuffp);
1202                return 0;
1203        }
1204        request_region((unsigned int) stuffp->wreg_data,
1205                       MCDX_IO_SIZE, "mcdx");
1206
1207        xtrace(INIT, "init() get garbage\n");
1208        {
1209                int i;
1210                mcdx_delay(stuffp, HZ / 2);
1211                for (i = 100; i; i--)
1212                        (void) inb((unsigned int) stuffp->rreg_status);
1213        }
1214
1215
1216#if WE_KNOW_WHY
1217        /* irq 11 -> channel register */
1218        outb(0x50, (unsigned int) stuffp->wreg_chn);
1219#endif
1220
1221        xtrace(INIT, "init() set non dma but irq mode\n");
1222        mcdx_config(stuffp, 1);
1223
1224        stuffp->minor = drive;
1225
1226        sprintf(msg, " mcdx: Mitsumi CD-ROM installed at 0x%3p, irq %d."
1227                " (Firmware version %c %x)\n",
1228                stuffp->wreg_data, stuffp->irq, version.code, version.ver);
1229        mcdx_stuffp[drive] = stuffp;
1230        xtrace(INIT, "init() mcdx_stuffp[%d] = %p\n", drive, stuffp);
1231        mcdx_info.dev = MKDEV(MAJOR_NR, 0);
1232        if (register_cdrom(&mcdx_info) != 0) {
1233                printk("Cannot register Mitsumi CD-ROM!\n");
1234                release_region((unsigned long) stuffp->wreg_data,
1235                               MCDX_IO_SIZE);
1236                free_irq(stuffp->irq, NULL);
1237                kfree(stuffp);
1238                if (devfs_unregister_blkdev(MAJOR_NR, "mcdx") != 0)
1239                        xwarn("cleanup() unregister_blkdev() failed\n");
1240                blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
1241                return 2;
1242        }
1243        devfs_plain_cdrom(&mcdx_info, &mcdx_bdops);
1244        printk(msg);
1245        return 0;
1246}
1247
1248int __init mcdx_init(void)
1249{
1250        int drive;
1251#ifdef MODULE
1252        xwarn("Version 2.14(hs) for " UTS_RELEASE "\n");
1253#else
1254        xwarn("Version 2.14(hs) \n");
1255#endif
1256
1257        xwarn("$Id: mcdx.c,v 1.21 1997/01/26 07:12:59 davem Exp $\n");
1258
1259        /* zero the pointer array */
1260        for (drive = 0; drive < MCDX_NDRIVES; drive++)
1261                mcdx_stuffp[drive] = NULL;
1262
1263        /* do the initialisation */
1264        for (drive = 0; drive < MCDX_NDRIVES; drive++) {
1265                switch (mcdx_init_drive(drive)) {
1266                case 2:
1267                        return -EIO;
1268                case 1:
1269                        break;
1270                }
1271        }
1272        return 0;
1273}
1274
1275static int mcdx_transfer(struct s_drive_stuff *stuffp,
1276              char *p, int sector, int nr_sectors)
1277/*      This seems to do the actually transfer.  But it does more.  It
1278        keeps track of errors occurred and will (if possible) fall back
1279        to single speed on error.
1280        Return: -1 on timeout or other error
1281                        else status byte (as in stuff->st) */
1282{
1283        int ans;
1284
1285        ans = mcdx_xfer(stuffp, p, sector, nr_sectors);
1286        return ans;
1287#if FALLBACK
1288        if (-1 == ans)
1289                stuffp->readerrs++;
1290        else
1291                return ans;
1292
1293        if (stuffp->readerrs && stuffp->readcmd == READ1X) {
1294                xwarn("XXX Already reading 1x -- no chance\n");
1295                return -1;
1296        }
1297
1298        xwarn("XXX Fallback to 1x\n");
1299
1300        stuffp->readcmd = READ1X;
1301        return mcdx_transfer(stuffp, p, sector, nr_sectors);
1302#endif
1303
1304}
1305
1306
1307static int mcdx_xfer(struct s_drive_stuff *stuffp,
1308                     char *p, int sector, int nr_sectors)
1309/*      This does actually the transfer from the drive.
1310        Return: -1 on timeout or other error
1311                        else status byte (as in stuff->st) */
1312{
1313        int border;
1314        int done = 0;
1315        long timeout;
1316
1317        if (stuffp->audio) {
1318                xwarn("Attempt to read from audio CD.\n");
1319                return -1;
1320        }
1321
1322        if (!stuffp->readcmd) {
1323                xinfo("Can't transfer from missing disk.\n");
1324                return -1;
1325        }
1326
1327        while (stuffp->lock) {
1328                interruptible_sleep_on(&stuffp->lockq);
1329        }
1330
1331        if (stuffp->valid && (sector >= stuffp->pending)
1332            && (sector < stuffp->low_border)) {
1333
1334                /* All (or at least a part of the sectors requested) seems
1335                   * to be already requested, so we don't need to bother the
1336                   * drive with new requests ...
1337                   * Wait for the drive become idle, but first
1338                   * check for possible occurred errors --- the drive
1339                   * seems to report them asynchronously */
1340
1341
1342                border = stuffp->high_border < (border =
1343                                                sector + nr_sectors)
1344                    ? stuffp->high_border : border;
1345
1346                stuffp->lock = current->pid;
1347
1348                do {
1349
1350                        while (stuffp->busy) {
1351
1352                                timeout =
1353                                    interruptible_sleep_on_timeout
1354                                    (&stuffp->busyq, 5 * HZ);
1355
1356                                if (!stuffp->introk) {
1357                                        xtrace(XFER,
1358                                               "error via interrupt\n");
1359                                } else if (!timeout) {
1360                                        xtrace(XFER, "timeout\n");
1361                                } else if (signal_pending(current)) {
1362                                        xtrace(XFER, "signal\n");
1363                                } else
1364                                        continue;
1365
1366                                stuffp->lock = 0;
1367                                stuffp->busy = 0;
1368                                stuffp->valid = 0;
1369
1370                                wake_up_interruptible(&stuffp->lockq);
1371                                xtrace(XFER, "transfer() done (-1)\n");
1372                                return -1;
1373                        }
1374
1375                        /* check if we need to set the busy flag (as we
1376                         * expect an interrupt */
1377                        stuffp->busy = (3 == (stuffp->pending & 3));
1378
1379                        /* Test if it's the first sector of a block,
1380                         * there we have to skip some bytes as we read raw data */
1381                        if (stuffp->xa && (0 == (stuffp->pending & 3))) {
1382                                const int HEAD =
1383                                    CD_FRAMESIZE_RAW - CD_XA_TAIL -
1384                                    CD_FRAMESIZE;
1385                                insb((unsigned int) stuffp->rreg_data, p,
1386                                     HEAD);
1387                        }
1388
1389                        /* now actually read the data */
1390                        insb((unsigned int) stuffp->rreg_data, p, 512);
1391
1392                        /* test if it's the last sector of a block,
1393                         * if so, we have to handle XA special */
1394                        if ((3 == (stuffp->pending & 3)) && stuffp->xa) {
1395                                char dummy[CD_XA_TAIL];
1396                                insb((unsigned int) stuffp->rreg_data,
1397                                     &dummy[0], CD_XA_TAIL);
1398                        }
1399
1400                        if (stuffp->pending == sector) {
1401                                p += 512;
1402                                done++;
1403                                sector++;
1404                        }
1405                } while (++(stuffp->pending) < border);
1406
1407                stuffp->lock = 0;
1408                wake_up_interruptible(&stuffp->lockq);
1409
1410        } else {
1411
1412                /* The requested sector(s) is/are out of the
1413                 * already requested range, so we have to bother the drive
1414                 * with a new request. */
1415
1416                static unsigned char cmd[] = {
1417                        0,
1418                        0, 0, 0,
1419                        0, 0, 0
1420                };
1421
1422                cmd[0] = stuffp->readcmd;
1423
1424                /* The numbers held in ->pending, ..., should be valid */
1425                stuffp->valid = 1;
1426                stuffp->pending = sector & ~3;
1427
1428                /* do some sanity checks */
1429                if (stuffp->pending > stuffp->lastsector) {
1430                        xwarn
1431                            ("transfer() sector %d from nirvana requested.\n",
1432                             stuffp->pending);
1433                        stuffp->status = MCDX_ST_EOM;
1434                        stuffp->valid = 0;
1435                        xtrace(XFER, "transfer() done (-1)\n");
1436                        return -1;
1437                }
1438
1439                if ((stuffp->low_border = stuffp->pending + DIRECT_SIZE)
1440                    > stuffp->lastsector + 1) {
1441                        xtrace(XFER, "cut low_border\n");
1442                        stuffp->low_border = stuffp->lastsector + 1;
1443                }
1444                if ((stuffp->high_border = stuffp->pending + REQUEST_SIZE)
1445                    > stuffp->lastsector + 1) {
1446                        xtrace(XFER, "cut high_border\n");
1447                        stuffp->high_border = stuffp->lastsector + 1;
1448                }
1449
1450                {               /* Convert the sector to be requested to MSF format */
1451                        struct cdrom_msf0 pending;
1452                        log2msf(stuffp->pending / 4, &pending);
1453                        cmd[1] = pending.minute;
1454                        cmd[2] = pending.second;
1455                        cmd[3] = pending.frame;
1456                }
1457
1458                cmd[6] =
1459                    (unsigned
1460                     char) ((stuffp->high_border - stuffp->pending) / 4);
1461                xtrace(XFER, "[%2d]\n", cmd[6]);
1462
1463                stuffp->busy = 1;
1464                /* Now really issue the request command */
1465                outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd);
1466
1467        }
1468#ifdef AK2
1469        if (stuffp->int_err) {
1470                stuffp->valid = 0;
1471                stuffp->int_err = 0;
1472                return -1;
1473        }
1474#endif                          /* AK2 */
1475
1476        stuffp->low_border = (stuffp->low_border +=
1477                              done) <
1478            stuffp->high_border ? stuffp->low_border : stuffp->high_border;
1479
1480        return done;
1481}
1482
1483
1484/*      Access to elements of the mcdx_drive_map members */
1485
1486static char *port(int *ip)
1487{
1488        return (char *) ip[0];
1489}
1490static int irq(int *ip)
1491{
1492        return ip[1];
1493}
1494
1495/*      Misc number converters */
1496
1497static unsigned int bcd2uint(unsigned char c)
1498{
1499        return (c >> 4) * 10 + (c & 0x0f);
1500}
1501
1502static unsigned int uint2bcd(unsigned int ival)
1503{
1504        return ((ival / 10) << 4) | (ival % 10);
1505}
1506
1507static void log2msf(unsigned int l, struct cdrom_msf0 *pmsf)
1508{
1509        l += CD_MSF_OFFSET;
1510        pmsf->minute = uint2bcd(l / 4500), l %= 4500;
1511        pmsf->second = uint2bcd(l / 75);
1512        pmsf->frame = uint2bcd(l % 75);
1513}
1514
1515static unsigned int msf2log(const struct cdrom_msf0 *pmsf)
1516{
1517        return bcd2uint(pmsf->frame)
1518            + bcd2uint(pmsf->second) * 75
1519            + bcd2uint(pmsf->minute) * 4500 - CD_MSF_OFFSET;
1520}
1521
1522int mcdx_readtoc(struct s_drive_stuff *stuffp)
1523/*  Read the toc entries from the CD,
1524 *  Return: -1 on failure, else 0 */
1525{
1526
1527        if (stuffp->toc) {
1528                xtrace(READTOC, "ioctl() toc already read\n");
1529                return 0;
1530        }
1531
1532        xtrace(READTOC, "ioctl() readtoc for %d tracks\n",
1533               stuffp->di.n_last - stuffp->di.n_first + 1);
1534
1535        if (-1 == mcdx_hold(stuffp, 1))
1536                return -1;
1537
1538        xtrace(READTOC, "ioctl() tocmode\n");
1539        if (-1 == mcdx_setdrivemode(stuffp, TOC, 1))
1540                return -EIO;
1541
1542        /* all seems to be ok so far ... malloc */
1543        {
1544                int size;
1545                size =
1546                    sizeof(struct s_subqcode) * (stuffp->di.n_last -
1547                                                 stuffp->di.n_first + 2);
1548
1549                xtrace(MALLOC, "ioctl() malloc %d bytes\n", size);
1550                stuffp->toc = kmalloc(size, GFP_KERNEL);
1551                if (!stuffp->toc) {
1552                        xwarn("Cannot malloc %d bytes for toc\n", size);
1553                        mcdx_setdrivemode(stuffp, DATA, 1);
1554                        return -EIO;
1555                }
1556        }
1557
1558        /* now read actually the index */
1559        {
1560                int trk;
1561                int retries;
1562
1563                for (trk = 0;
1564                     trk < (stuffp->di.n_last - stuffp->di.n_first + 1);
1565                     trk++)
1566                        stuffp->toc[trk].index = 0;
1567
1568                for (retries = 300; retries; retries--) {       /* why 300? */
1569                        struct s_subqcode q;
1570                        unsigned int idx;
1571
1572                        if (-1 == mcdx_requestsubqcode(stuffp, &q, 1)) {
1573                                mcdx_setdrivemode(stuffp, DATA, 1);
1574                                return -EIO;
1575                        }
1576
1577                        idx = bcd2uint(q.index);
1578
1579                        if ((idx > 0)
1580                            && (idx <= stuffp->di.n_last)
1581                            && (q.tno == 0)
1582                            && (stuffp->toc[idx - stuffp->di.n_first].
1583                                index == 0)) {
1584                                stuffp->toc[idx - stuffp->di.n_first] = q;
1585                                xtrace(READTOC,
1586                                       "ioctl() toc idx %d (trk %d)\n",
1587                                       idx, trk);
1588                                trk--;
1589                        }
1590                        if (trk == 0)
1591                                break;
1592                }
1593                memset(&stuffp->
1594                       toc[stuffp->di.n_last - stuffp->di.n_first + 1], 0,
1595                       sizeof(stuffp->toc[0]));
1596                stuffp->toc[stuffp->di.n_last - stuffp->di.n_first +
1597                            1].dt = stuffp->di.msf_leadout;
1598        }
1599
1600        /* unset toc mode */
1601        xtrace(READTOC, "ioctl() undo toc mode\n");
1602        if (-1 == mcdx_setdrivemode(stuffp, DATA, 2))
1603                return -EIO;
1604
1605#if MCDX_DEBUG && READTOC
1606        {
1607                int trk;
1608                for (trk = 0;
1609                     trk < (stuffp->di.n_last - stuffp->di.n_first + 2);
1610                     trk++)
1611                        xtrace(READTOC, "ioctl() %d readtoc %02x %02x %02x"
1612                               "  %02x:%02x.%02x  %02x:%02x.%02x\n",
1613                               trk + stuffp->di.n_first,
1614                               stuffp->toc[trk].control,
1615                               stuffp->toc[trk].tno,
1616                               stuffp->toc[trk].index,
1617                               stuffp->toc[trk].tt.minute,
1618                               stuffp->toc[trk].tt.second,
1619                               stuffp->toc[trk].tt.frame,
1620                               stuffp->toc[trk].dt.minute,
1621                               stuffp->toc[trk].dt.second,
1622                               stuffp->toc[trk].dt.frame);
1623        }
1624#endif
1625
1626        return 0;
1627}
1628
1629static int
1630mcdx_playmsf(struct s_drive_stuff *stuffp, const struct cdrom_msf *msf)
1631{
1632        unsigned char cmd[7] = {
1633                0, 0, 0, 0, 0, 0, 0
1634        };
1635
1636        if (!stuffp->readcmd) {
1637                xinfo("Can't play from missing disk.\n");
1638                return -1;
1639        }
1640
1641        cmd[0] = stuffp->playcmd;
1642
1643        cmd[1] = msf->cdmsf_min0;
1644        cmd[2] = msf->cdmsf_sec0;
1645        cmd[3] = msf->cdmsf_frame0;
1646        cmd[4] = msf->cdmsf_min1;
1647        cmd[5] = msf->cdmsf_sec1;
1648        cmd[6] = msf->cdmsf_frame1;
1649
1650        xtrace(PLAYMSF, "ioctl(): play %x "
1651               "%02x:%02x:%02x -- %02x:%02x:%02x\n",
1652               cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]);
1653
1654        outsb((unsigned int) stuffp->wreg_data, cmd, sizeof cmd);
1655
1656        if (-1 == mcdx_getval(stuffp, 3 * HZ, 0, NULL)) {
1657                xwarn("playmsf() timeout\n");
1658                return -1;
1659        }
1660
1661        stuffp->audiostatus = CDROM_AUDIO_PLAY;
1662        return 0;
1663}
1664
1665static int
1666mcdx_playtrk(struct s_drive_stuff *stuffp, const struct cdrom_ti *ti)
1667{
1668        struct s_subqcode *p;
1669        struct cdrom_msf msf;
1670
1671        if (-1 == mcdx_readtoc(stuffp))
1672                return -1;
1673
1674        if (ti)
1675                p = &stuffp->toc[ti->cdti_trk0 - stuffp->di.n_first];
1676        else
1677                p = &stuffp->start;
1678
1679        msf.cdmsf_min0 = p->dt.minute;
1680        msf.cdmsf_sec0 = p->dt.second;
1681        msf.cdmsf_frame0 = p->dt.frame;
1682
1683        if (ti) {
1684                p = &stuffp->toc[ti->cdti_trk1 - stuffp->di.n_first + 1];
1685                stuffp->stop = *p;
1686        } else
1687                p = &stuffp->stop;
1688
1689        msf.cdmsf_min1 = p->dt.minute;
1690        msf.cdmsf_sec1 = p->dt.second;
1691        msf.cdmsf_frame1 = p->dt.frame;
1692
1693        return mcdx_playmsf(stuffp, &msf);
1694}
1695
1696
1697/* Drive functions ************************************************/
1698
1699static int mcdx_tray_move(struct cdrom_device_info *cdi, int position)
1700{
1701        struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(cdi->dev)];
1702
1703        if (!stuffp->present)
1704                return -ENXIO;
1705        if (!(stuffp->present & DOOR))
1706                return -ENOSYS;
1707
1708        if (position)           /* 1: eject */
1709                return mcdx_talk(stuffp, "\xf6", 1, NULL, 1, 5 * HZ, 3);
1710        else                    /* 0: close */
1711                return mcdx_talk(stuffp, "\xf8", 1, NULL, 1, 5 * HZ, 3);
1712        return 1;
1713}
1714
1715static int mcdx_stop(struct s_drive_stuff *stuffp, int tries)
1716{
1717        return mcdx_talk(stuffp, "\xf0", 1, NULL, 1, 2 * HZ, tries);
1718}
1719
1720static int mcdx_hold(struct s_drive_stuff *stuffp, int tries)
1721{
1722        return mcdx_talk(stuffp, "\x70", 1, NULL, 1, 2 * HZ, tries);
1723}
1724
1725static int mcdx_requestsubqcode(struct s_drive_stuff *stuffp,
1726                     struct s_subqcode *sub, int tries)
1727{
1728        char buf[11];
1729        int ans;
1730
1731        if (-1 == (ans = mcdx_talk(stuffp, "\x20", 1, buf, sizeof(buf),
1732                                   2 * HZ, tries)))
1733                return -1;
1734        sub->control = buf[1];
1735        sub->tno = buf[2];
1736        sub->index = buf[3];
1737        sub->tt.minute = buf[4];
1738        sub->tt.second = buf[5];
1739        sub->tt.frame = buf[6];
1740        sub->dt.minute = buf[8];
1741        sub->dt.second = buf[9];
1742        sub->dt.frame = buf[10];
1743
1744        return ans;
1745}
1746
1747static int mcdx_requestmultidiskinfo(struct s_drive_stuff *stuffp,
1748                          struct s_multi *multi, int tries)
1749{
1750        char buf[5];
1751        int ans;
1752
1753        if (stuffp->present & MULTI) {
1754                ans =
1755                    mcdx_talk(stuffp, "\x11", 1, buf, sizeof(buf), 2 * HZ,
1756                              tries);
1757                multi->multi = buf[1];
1758                multi->msf_last.minute = buf[2];
1759                multi->msf_last.second = buf[3];
1760                multi->msf_last.frame = buf[4];
1761                return ans;
1762        } else {
1763                multi->multi = 0;
1764                return 0;
1765        }
1766}
1767
1768static int mcdx_requesttocdata(struct s_drive_stuff *stuffp, struct s_diskinfo *info,
1769                    int tries)
1770{
1771        char buf[9];
1772        int ans;
1773        ans =
1774            mcdx_talk(stuffp, "\x10", 1, buf, sizeof(buf), 2 * HZ, tries);
1775        if (ans == -1) {
1776                info->n_first = 0;
1777                info->n_last = 0;
1778        } else {
1779                info->n_first = bcd2uint(buf[1]);
1780                info->n_last = bcd2uint(buf[2]);
1781                info->msf_leadout.minute = buf[3];
1782                info->msf_leadout.second = buf[4];
1783                info->msf_leadout.frame = buf[5];
1784                info->msf_first.minute = buf[6];
1785                info->msf_first.second = buf[7];
1786                info->msf_first.frame = buf[8];
1787        }
1788        return ans;
1789}
1790
1791static int mcdx_setdrivemode(struct s_drive_stuff *stuffp, enum drivemodes mode,
1792                  int tries)
1793{
1794        char cmd[2];
1795        int ans;
1796
1797        xtrace(HW, "setdrivemode() %d\n", mode);
1798
1799        if (-1 == (ans = mcdx_talk(stuffp, "\xc2", 1, cmd, sizeof(cmd), 5 * HZ, tries)))
1800                return -1;
1801
1802        switch (mode) {
1803        case TOC:
1804                cmd[1] |= 0x04;
1805                break;
1806        case DATA:
1807                cmd[1] &= ~0x04;
1808                break;
1809        case RAW:
1810                cmd[1] |= 0x40;
1811                break;
1812        case COOKED:
1813                cmd[1] &= ~0x40;
1814                break;
1815        default:
1816                break;
1817        }
1818        cmd[0] = 0x50;
1819        return mcdx_talk(stuffp, cmd, 2, NULL, 1, 5 * HZ, tries);
1820}
1821
1822static int mcdx_setdatamode(struct s_drive_stuff *stuffp, enum datamodes mode,
1823                 int tries)
1824{
1825        unsigned char cmd[2] = { 0xa0 };
1826        xtrace(HW, "setdatamode() %d\n", mode);
1827        switch (mode) {
1828        case MODE0:
1829                cmd[1] = 0x00;
1830                break;
1831        case MODE1:
1832                cmd[1] = 0x01;
1833                break;
1834        case MODE2:
1835                cmd[1] = 0x02;
1836                break;
1837        default:
1838                return -EINVAL;
1839        }
1840        return mcdx_talk(stuffp, cmd, 2, NULL, 1, 5 * HZ, tries);
1841}
1842
1843static int mcdx_config(struct s_drive_stuff *stuffp, int tries)
1844{
1845        char cmd[4];
1846
1847        xtrace(HW, "config()\n");
1848
1849        cmd[0] = 0x90;
1850
1851        cmd[1] = 0x10;          /* irq enable */
1852        cmd[2] = 0x05;          /* pre, err irq enable */
1853
1854        if (-1 == mcdx_talk(stuffp, cmd, 3, NULL, 1, 1 * HZ, tries))
1855                return -1;
1856
1857        cmd[1] = 0x02;          /* dma select */
1858        cmd[2] = 0x00;          /* no dma */
1859
1860        return mcdx_talk(stuffp, cmd, 3, NULL, 1, 1 * HZ, tries);
1861}
1862
1863static int mcdx_requestversion(struct s_drive_stuff *stuffp, struct s_version *ver,
1864                    int tries)
1865{
1866        char buf[3];
1867        int ans;
1868
1869        if (-1 == (ans = mcdx_talk(stuffp, "\xdc",
1870                                   1, buf, sizeof(buf), 2 * HZ, tries)))
1871                return ans;
1872
1873        ver->code = buf[1];
1874        ver->ver = buf[2];
1875
1876        return ans;
1877}
1878
1879static int mcdx_reset(struct s_drive_stuff *stuffp, enum resetmodes mode, int tries)
1880{
1881        if (mode == HARD) {
1882                outb(0, (unsigned int) stuffp->wreg_chn);       /* no dma, no irq -> hardware */
1883                outb(0, (unsigned int) stuffp->wreg_reset);     /* hw reset */
1884                return 0;
1885        } else
1886                return mcdx_talk(stuffp, "\x60", 1, NULL, 1, 5 * HZ, tries);
1887}
1888
1889static int mcdx_lockdoor(struct cdrom_device_info *cdi, int lock)
1890{
1891        struct s_drive_stuff *stuffp = mcdx_stuffp[MINOR(cdi->dev)];
1892        char cmd[2] = { 0xfe };
1893
1894        if (!(stuffp->present & DOOR))
1895                return -ENOSYS;
1896        if (stuffp->present & DOOR) {
1897                cmd[1] = lock ? 0x01 : 0x00;
1898                return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 1, 5 * HZ, 3);
1899        } else
1900                return 0;
1901}
1902
1903static int mcdx_getstatus(struct s_drive_stuff *stuffp, int tries)
1904{
1905        return mcdx_talk(stuffp, "\x40", 1, NULL, 1, 5 * HZ, tries);
1906}
1907
1908static int
1909mcdx_getval(struct s_drive_stuff *stuffp, int to, int delay, char *buf)
1910{
1911        unsigned long timeout = to + jiffies;
1912        char c;
1913
1914        if (!buf)
1915                buf = &c;
1916
1917        while (inb((unsigned int) stuffp->rreg_status) & MCDX_RBIT_STEN) {
1918                if (time_after(jiffies, timeout))
1919                        return -1;
1920                mcdx_delay(stuffp, delay);
1921        }
1922
1923        *buf = (unsigned char) inb((unsigned int) stuffp->rreg_data) & 0xff;
1924
1925        return 0;
1926}
1927
1928static int mcdx_setattentuator(struct s_drive_stuff *stuffp,
1929                    struct cdrom_volctrl *vol, int tries)
1930{
1931        char cmd[5];
1932        cmd[0] = 0xae;
1933        cmd[1] = vol->channel0;
1934        cmd[2] = 0;
1935        cmd[3] = vol->channel1;
1936        cmd[4] = 0;
1937
1938        return mcdx_talk(stuffp, cmd, sizeof(cmd), NULL, 5, 200, tries);
1939}
1940
1941MODULE_LICENSE("GPL");
1942
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.