linux-old/drivers/cdrom/mcd.c
<<
>>
Prefs
   1/*
   2        linux/kernel/blk_drv/mcd.c - Mitsumi CDROM driver
   3
   4        Copyright (C) 1992  Martin Harriss
   5        Portions Copyright (C) 2001 Red Hat
   6
   7        martin@bdsi.com (no longer valid - where are you now, Martin?)
   8
   9        This program is free software; you can redistribute it and/or modify
  10        it under the terms of the GNU General Public License as published by
  11        the Free Software Foundation; either version 2, or (at your option)
  12        any later version.
  13
  14        This program is distributed in the hope that it will be useful,
  15        but WITHOUT ANY WARRANTY; without even the implied warranty of
  16        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17        GNU General Public License for more details.
  18
  19        You should have received a copy of the GNU General Public License
  20        along with this program; if not, write to the Free Software
  21        Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22
  23        HISTORY
  24
  25        0.1     First attempt - internal use only
  26        0.2     Cleaned up delays and use of timer - alpha release
  27        0.3     Audio support added
  28        0.3.1 Changes for mitsumi CRMC LU005S march version
  29                   (stud11@cc4.kuleuven.ac.be)
  30        0.3.2 bug fixes to the ioctls and merged with ALPHA0.99-pl12
  31                   (Jon Tombs <jon@robots.ox.ac.uk>)
  32        0.3.3 Added more #defines and mcd_setup()
  33                   (Jon Tombs <jon@gtex02.us.es>)
  34
  35        October 1993 Bernd Huebner and Ruediger Helsch, Unifix Software GmbH,
  36        Braunschweig, Germany: rework to speed up data read operation.
  37        Also enabled definition of irq and address from bootstrap, using the
  38        environment.
  39        November 93 added code for FX001 S,D (single & double speed).
  40        February 94 added code for broken M 5/6 series of 16-bit single speed.
  41
  42
  43        0.4   
  44        Added support for loadable MODULEs, so mcd can now also be loaded by 
  45        insmod and removed by rmmod during runtime.
  46        Werner Zimmermann (zimmerma@rz.fht-esslingen.de), Mar. 26, 95
  47
  48        0.5
  49        I added code for FX001 D to drop from double speed to single speed 
  50        when encountering errors... this helps with some "problematic" CD's
  51        that are supposedly "OUT OF TOLERANCE" (but are really shitty presses!)
  52        severely scratched, or possibly slightly warped! I have noticed that
  53        the Mitsumi 2x/4x drives are just less tolerant and the firmware is 
  54        not smart enough to drop speed, so let's just kludge it with software!
  55        ****** THE 4X SPEED MITSUMI DRIVES HAVE THE SAME PROBLEM!!!!!! ******
  56        Anyone want to "DONATE" one to me?! ;) I hear sometimes they are
  57        even WORSE! ;)
  58        ** HINT... HINT... TAKE NOTES MITSUMI This could save some hassles with
  59        certain "large" CD's that have data on the outside edge in your 
  60        DOS DRIVERS .... Accuracy counts... speed is secondary ;)
  61        17 June 95 Modifications By Andrew J. Kroll <ag784@freenet.buffalo.edu>
  62        07 July 1995 Modifications by Andrew J. Kroll
  63
  64        Bjorn Ekwall <bj0rn@blox.se> added unregister_blkdev to mcd_init()
  65
  66        Michael K. Johnson <johnsonm@redhat.com> added retries on open
  67        for slow drives which take a while to recognize that they contain
  68        a CD.
  69
  70        November 1997 -- ported to the Uniform CD-ROM driver by Erik Andersen.
  71        March    1999 -- made io base and irq CONFIG_ options (Tigran Aivazian).
  72        
  73        November 1999 -- Make kernel-parameter implementation work with 2.3.x 
  74                         Removed init_module & cleanup_module in favor of 
  75                         module_init & module_exit.
  76                         Torben Mathiasen <tmm@image.dk>
  77                
  78        September 2001 - Reformatted and cleaned up the code
  79                         Alan Cox <alan@redhat.com>                      
  80*/
  81
  82#include <linux/module.h>
  83
  84#include <linux/errno.h>
  85#include <linux/signal.h>
  86#include <linux/sched.h>
  87#include <linux/mm.h>
  88#include <linux/timer.h>
  89#include <linux/fs.h>
  90#include <linux/kernel.h>
  91#include <linux/devfs_fs_kernel.h>
  92#include <linux/cdrom.h>
  93#include <linux/ioport.h>
  94#include <linux/string.h>
  95#include <linux/delay.h>
  96#include <linux/init.h>
  97#include <linux/config.h>
  98
  99/* #define REALLY_SLOW_IO  */
 100#include <asm/system.h>
 101#include <asm/io.h>
 102#include <asm/uaccess.h>
 103
 104#define MAJOR_NR MITSUMI_CDROM_MAJOR
 105#include <linux/blk.h>
 106
 107#define mcd_port mcd            /* for compatible parameter passing with "insmod" */
 108#include "mcd.h"
 109
 110static int mcd_blocksizes[1];
 111
 112
 113/* I added A flag to drop to 1x speed if too many errors 0 = 1X ; 1 = 2X */
 114static int mcdDouble;
 115
 116/* How many sectors to hold at 1x speed counter */
 117static int mcd1xhold;
 118
 119/* Is the drive connected properly and responding?? */
 120static int mcdPresent;
 121
 122#define QUICK_LOOP_DELAY udelay(45)     /* use udelay */
 123#define QUICK_LOOP_COUNT 20
 124
 125#define CURRENT_VALID \
 126(!QUEUE_EMPTY && MAJOR(CURRENT -> rq_dev) == MAJOR_NR && CURRENT -> cmd == READ \
 127&& CURRENT -> sector != -1)
 128
 129#define MFL_STATUSorDATA (MFL_STATUS | MFL_DATA)
 130#define MCD_BUF_SIZ 16
 131static volatile int mcd_transfer_is_active;
 132static char mcd_buf[2048 * MCD_BUF_SIZ];        /* buffer for block size conversion */
 133static volatile int mcd_buf_bn[MCD_BUF_SIZ], mcd_next_bn;
 134static volatile int mcd_buf_in, mcd_buf_out = -1;
 135static volatile int mcd_error;
 136static int mcd_open_count;
 137enum mcd_state_e {
 138        MCD_S_IDLE,             /* 0 */
 139        MCD_S_START,            /* 1 */
 140        MCD_S_MODE,             /* 2 */
 141        MCD_S_READ,             /* 3 */
 142        MCD_S_DATA,             /* 4 */
 143        MCD_S_STOP,             /* 5 */
 144        MCD_S_STOPPING          /* 6 */
 145};
 146static volatile enum mcd_state_e mcd_state = MCD_S_IDLE;
 147static int mcd_mode = -1;
 148static int MCMD_DATA_READ = MCMD_PLAY_READ;
 149
 150#define READ_TIMEOUT 3000
 151
 152int mitsumi_bug_93_wait;
 153
 154static short mcd_port = CONFIG_MCD_BASE;        /* used as "mcd" by "insmod" */
 155static int mcd_irq = CONFIG_MCD_IRQ;    /* must directly follow mcd_port */
 156MODULE_PARM(mcd, "1-2i");
 157
 158static int McdTimeout, McdTries;
 159static DECLARE_WAIT_QUEUE_HEAD(mcd_waitq);
 160
 161static struct mcd_DiskInfo DiskInfo;
 162static struct mcd_Toc Toc[MAX_TRACKS];
 163static struct mcd_Play_msf mcd_Play;
 164
 165static int audioStatus;
 166static char mcdDiskChanged;
 167static char tocUpToDate;
 168static char mcdVersion;
 169
 170static void mcd_transfer(void);
 171static void mcd_poll(unsigned long dummy);
 172static void mcd_invalidate_buffers(void);
 173static void hsg2msf(long hsg, struct msf *msf);
 174static void bin2bcd(unsigned char *p);
 175static int bcd2bin(unsigned char bcd);
 176static int mcdStatus(void);
 177static void sendMcdCmd(int cmd, struct mcd_Play_msf *params);
 178static int getMcdStatus(int timeout);
 179static int GetQChannelInfo(struct mcd_Toc *qp);
 180static int updateToc(void);
 181static int GetDiskInfo(void);
 182static int GetToc(void);
 183static int getValue(unsigned char *result);
 184static int mcd_open(struct cdrom_device_info *cdi, int purpose);
 185static void mcd_release(struct cdrom_device_info *cdi);
 186static int mcd_media_changed(struct cdrom_device_info *cdi, int disc_nr);
 187static int mcd_tray_move(struct cdrom_device_info *cdi, int position);
 188int mcd_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
 189                    void *arg);
 190int mcd_drive_status(struct cdrom_device_info *cdi, int slot_nr);
 191
 192struct block_device_operations mcd_bdops =
 193{
 194        owner:                  THIS_MODULE,
 195        open:                   cdrom_open,
 196        release:                cdrom_release,
 197        ioctl:                  cdrom_ioctl,
 198        check_media_change:     cdrom_media_changed,
 199};
 200
 201static struct timer_list mcd_timer;
 202
 203static struct cdrom_device_ops mcd_dops = {
 204        open:mcd_open,
 205        release:mcd_release,
 206        drive_status:mcd_drive_status,
 207        media_changed:mcd_media_changed,
 208        tray_move:mcd_tray_move,
 209        audio_ioctl:mcd_audio_ioctl,
 210        capability:CDC_OPEN_TRAY | CDC_MEDIA_CHANGED |
 211            CDC_PLAY_AUDIO | CDC_DRIVE_STATUS,
 212};
 213
 214static struct cdrom_device_info mcd_info = {
 215        ops:&mcd_dops,
 216        speed:2,
 217        capacity:1,
 218        name:"mcd",
 219};
 220
 221#ifndef MODULE
 222static int __init mcd_setup(char *str)
 223{
 224        int ints[9];
 225
 226        (void) get_options(str, ARRAY_SIZE(ints), ints);
 227
 228        if (ints[0] > 0)
 229                mcd_port = ints[1];
 230        if (ints[0] > 1)
 231                mcd_irq = ints[2];
 232        if (ints[0] > 2)
 233                mitsumi_bug_93_wait = ints[3];
 234
 235        return 1;
 236}
 237
 238__setup("mcd=", mcd_setup);
 239
 240#endif                          /* MODULE */
 241
 242static int mcd_media_changed(struct cdrom_device_info *cdi, int disc_nr)
 243{
 244        return 0;
 245}
 246
 247
 248/*
 249 * Do a 'get status' command and get the result.  Only use from the top half
 250 * because it calls 'getMcdStatus' which sleeps.
 251 */
 252
 253static int statusCmd(void)
 254{
 255        int st = -1, retry;
 256
 257        for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) {
 258                /* send get-status cmd */
 259                outb(MCMD_GET_STATUS, MCDPORT(0));
 260
 261                st = getMcdStatus(MCD_STATUS_DELAY);
 262                if (st != -1)
 263                        break;
 264        }
 265
 266        return st;
 267}
 268
 269
 270/*
 271 * Send a 'Play' command and get the status.  Use only from the top half.
 272 */
 273
 274static int mcdPlay(struct mcd_Play_msf *arg)
 275{
 276        int retry, st = -1;
 277
 278        for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) {
 279                sendMcdCmd(MCMD_PLAY_READ, arg);
 280                st = getMcdStatus(2 * MCD_STATUS_DELAY);
 281                if (st != -1)
 282                        break;
 283        }
 284
 285        return st;
 286}
 287
 288
 289static int mcd_tray_move(struct cdrom_device_info *cdi, int position)
 290{
 291        int i;
 292        if (position) {
 293                /*  Eject */
 294                /* all drives can at least stop! */
 295                if (audioStatus == CDROM_AUDIO_PLAY) {
 296                        outb(MCMD_STOP, MCDPORT(0));
 297                        i = getMcdStatus(MCD_STATUS_DELAY);
 298                }
 299
 300                audioStatus = CDROM_AUDIO_NO_STATUS;
 301
 302                outb(MCMD_EJECT, MCDPORT(0));
 303                /*
 304                 * the status (i) shows failure on all but the FX drives.
 305                 * But nothing we can do about that in software!
 306                 * So just read the status and forget it. - Jon.
 307                 */
 308                i = getMcdStatus(MCD_STATUS_DELAY);
 309                return 0;
 310        } else
 311                return -EINVAL;
 312}
 313
 314long msf2hsg(struct msf *mp)
 315{
 316        return bcd2bin(mp->frame) + bcd2bin(mp->sec) * 75 + bcd2bin(mp->min) * 4500 - 150;
 317}
 318
 319
 320int mcd_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
 321                    void *arg)
 322{
 323        int i, st;
 324        struct mcd_Toc qInfo;
 325        struct cdrom_ti *ti;
 326        struct cdrom_tochdr *tocHdr;
 327        struct cdrom_msf *msf;
 328        struct cdrom_subchnl *subchnl;
 329        struct cdrom_tocentry *entry;
 330        struct mcd_Toc *tocPtr;
 331        struct cdrom_volctrl *volctrl;
 332
 333        st = statusCmd();
 334        if (st < 0)
 335                return -EIO;
 336
 337        if (!tocUpToDate) {
 338                i = updateToc();
 339                if (i < 0)
 340                        return i;       /* error reading TOC */
 341        }
 342
 343        switch (cmd) {
 344        case CDROMSTART:        /* Spin up the drive */
 345                /* Don't think we can do this.  Even if we could,
 346                 * I think the drive times out and stops after a while
 347                 * anyway.  For now, ignore it.
 348                 */
 349
 350                return 0;
 351
 352        case CDROMSTOP: /* Spin down the drive */
 353                outb(MCMD_STOP, MCDPORT(0));
 354                i = getMcdStatus(MCD_STATUS_DELAY);
 355
 356                /* should we do anything if it fails? */
 357
 358                audioStatus = CDROM_AUDIO_NO_STATUS;
 359                return 0;
 360
 361        case CDROMPAUSE:        /* Pause the drive */
 362                if (audioStatus != CDROM_AUDIO_PLAY)
 363                        return -EINVAL;
 364
 365                outb(MCMD_STOP, MCDPORT(0));
 366                i = getMcdStatus(MCD_STATUS_DELAY);
 367
 368                if (GetQChannelInfo(&qInfo) < 0) {
 369                        /* didn't get q channel info */
 370
 371                        audioStatus = CDROM_AUDIO_NO_STATUS;
 372                        return 0;
 373                }
 374
 375                mcd_Play.start = qInfo.diskTime;        /* remember restart point */
 376
 377                audioStatus = CDROM_AUDIO_PAUSED;
 378                return 0;
 379
 380        case CDROMRESUME:       /* Play it again, Sam */
 381                if (audioStatus != CDROM_AUDIO_PAUSED)
 382                        return -EINVAL;
 383
 384                /* restart the drive at the saved position. */
 385
 386                i = mcdPlay(&mcd_Play);
 387                if (i < 0) {
 388                        audioStatus = CDROM_AUDIO_ERROR;
 389                        return -EIO;
 390                }
 391
 392                audioStatus = CDROM_AUDIO_PLAY;
 393                return 0;
 394
 395        case CDROMPLAYTRKIND:   /* Play a track.  This currently ignores index. */
 396
 397                ti = (struct cdrom_ti *) arg;
 398
 399                if (ti->cdti_trk0 < DiskInfo.first
 400                    || ti->cdti_trk0 > DiskInfo.last
 401                    || ti->cdti_trk1 < ti->cdti_trk0) {
 402                        return -EINVAL;
 403                }
 404
 405                if (ti->cdti_trk1 > DiskInfo.last)
 406                        ti->cdti_trk1 = DiskInfo.last;
 407
 408                mcd_Play.start = Toc[ti->cdti_trk0].diskTime;
 409                mcd_Play.end = Toc[ti->cdti_trk1 + 1].diskTime;
 410
 411#ifdef MCD_DEBUG
 412                printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n",
 413                       mcd_Play.start.min, mcd_Play.start.sec,
 414                       mcd_Play.start.frame, mcd_Play.end.min,
 415                       mcd_Play.end.sec, mcd_Play.end.frame);
 416#endif
 417
 418                i = mcdPlay(&mcd_Play);
 419                if (i < 0) {
 420                        audioStatus = CDROM_AUDIO_ERROR;
 421                        return -EIO;
 422                }
 423
 424                audioStatus = CDROM_AUDIO_PLAY;
 425                return 0;
 426
 427        case CDROMPLAYMSF:      /* Play starting at the given MSF address. */
 428
 429                if (audioStatus == CDROM_AUDIO_PLAY) {
 430                        outb(MCMD_STOP, MCDPORT(0));
 431                        i = getMcdStatus(MCD_STATUS_DELAY);
 432                        audioStatus = CDROM_AUDIO_NO_STATUS;
 433                }
 434
 435                msf = (struct cdrom_msf *) arg;
 436
 437                /* convert to bcd */
 438
 439                bin2bcd(&msf->cdmsf_min0);
 440                bin2bcd(&msf->cdmsf_sec0);
 441                bin2bcd(&msf->cdmsf_frame0);
 442                bin2bcd(&msf->cdmsf_min1);
 443                bin2bcd(&msf->cdmsf_sec1);
 444                bin2bcd(&msf->cdmsf_frame1);
 445
 446                mcd_Play.start.min = msf->cdmsf_min0;
 447                mcd_Play.start.sec = msf->cdmsf_sec0;
 448                mcd_Play.start.frame = msf->cdmsf_frame0;
 449                mcd_Play.end.min = msf->cdmsf_min1;
 450                mcd_Play.end.sec = msf->cdmsf_sec1;
 451                mcd_Play.end.frame = msf->cdmsf_frame1;
 452
 453#ifdef MCD_DEBUG
 454                printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n",
 455                       mcd_Play.start.min, mcd_Play.start.sec,
 456                       mcd_Play.start.frame, mcd_Play.end.min,
 457                       mcd_Play.end.sec, mcd_Play.end.frame);
 458#endif
 459
 460                i = mcdPlay(&mcd_Play);
 461                if (i < 0) {
 462                        audioStatus = CDROM_AUDIO_ERROR;
 463                        return -EIO;
 464                }
 465
 466                audioStatus = CDROM_AUDIO_PLAY;
 467                return 0;
 468
 469        case CDROMREADTOCHDR:   /* Read the table of contents header */
 470                tocHdr = (struct cdrom_tochdr *) arg;
 471                tocHdr->cdth_trk0 = DiskInfo.first;
 472                tocHdr->cdth_trk1 = DiskInfo.last;
 473                return 0;
 474
 475        case CDROMREADTOCENTRY: /* Read an entry in the table of contents */
 476                entry = (struct cdrom_tocentry *) arg;
 477                if (entry->cdte_track == CDROM_LEADOUT)
 478                        tocPtr = &Toc[DiskInfo.last - DiskInfo.first + 1];
 479
 480                else if (entry->cdte_track > DiskInfo.last
 481                         || entry->cdte_track < DiskInfo.first)
 482                        return -EINVAL;
 483
 484                else
 485                        tocPtr = &Toc[entry->cdte_track];
 486
 487                entry->cdte_adr = tocPtr->ctrl_addr;
 488                entry->cdte_ctrl = tocPtr->ctrl_addr >> 4;
 489
 490                if (entry->cdte_format == CDROM_LBA)
 491                        entry->cdte_addr.lba = msf2hsg(&tocPtr->diskTime);
 492
 493                else if (entry->cdte_format == CDROM_MSF) {
 494                        entry->cdte_addr.msf.minute =
 495                            bcd2bin(tocPtr->diskTime.min);
 496                        entry->cdte_addr.msf.second =
 497                            bcd2bin(tocPtr->diskTime.sec);
 498                        entry->cdte_addr.msf.frame =
 499                            bcd2bin(tocPtr->diskTime.frame);
 500                }
 501
 502                else
 503                        return -EINVAL;
 504
 505                return 0;
 506
 507        case CDROMSUBCHNL:      /* Get subchannel info */
 508
 509                subchnl = (struct cdrom_subchnl *) arg;
 510                if (GetQChannelInfo(&qInfo) < 0)
 511                        return -EIO;
 512
 513                subchnl->cdsc_audiostatus = audioStatus;
 514                subchnl->cdsc_adr = qInfo.ctrl_addr;
 515                subchnl->cdsc_ctrl = qInfo.ctrl_addr >> 4;
 516                subchnl->cdsc_trk = bcd2bin(qInfo.track);
 517                subchnl->cdsc_ind = bcd2bin(qInfo.pointIndex);
 518                subchnl->cdsc_absaddr.msf.minute = bcd2bin(qInfo.diskTime.min);
 519                subchnl->cdsc_absaddr.msf.second = bcd2bin(qInfo.diskTime.sec);
 520                subchnl->cdsc_absaddr.msf.frame  = bcd2bin(qInfo.diskTime.frame);
 521                subchnl->cdsc_reladdr.msf.minute = bcd2bin(qInfo.trackTime.min);
 522                subchnl->cdsc_reladdr.msf.second = bcd2bin(qInfo.trackTime.sec);
 523                subchnl->cdsc_reladdr.msf.frame  = bcd2bin(qInfo.trackTime.frame);
 524                return (0);
 525
 526        case CDROMVOLCTRL:      /* Volume control */
 527                volctrl = (struct cdrom_volctrl *) arg;
 528                outb(MCMD_SET_VOLUME, MCDPORT(0));
 529                outb(volctrl->channel0, MCDPORT(0));
 530                outb(255, MCDPORT(0));
 531                outb(volctrl->channel1, MCDPORT(0));
 532                outb(255, MCDPORT(0));
 533
 534                i = getMcdStatus(MCD_STATUS_DELAY);
 535                if (i < 0)
 536                        return -EIO;
 537
 538                {
 539                        char a, b, c, d;
 540
 541                        getValue(&a);
 542                        getValue(&b);
 543                        getValue(&c);
 544                        getValue(&d);
 545                }
 546
 547                return 0;
 548
 549        default:
 550                return -EINVAL;
 551        }
 552}
 553
 554/*
 555 * Take care of the different block sizes between cdrom and Linux.
 556 * When Linux gets variable block sizes this will probably go away.
 557 */
 558
 559static void mcd_transfer(void)
 560{
 561        if (CURRENT_VALID) {
 562                while (CURRENT->nr_sectors) {
 563                        int bn = CURRENT->sector / 4;
 564                        int i;
 565                        for (i = 0; i < MCD_BUF_SIZ && mcd_buf_bn[i] != bn;
 566                             ++i);
 567                        if (i < MCD_BUF_SIZ) {
 568                                int offs =(i * 4 + (CURRENT->sector & 3)) * 512;
 569                                int nr_sectors = 4 - (CURRENT->sector & 3);
 570                                if (mcd_buf_out != i) {
 571                                        mcd_buf_out = i;
 572                                        if (mcd_buf_bn[i] != bn) {
 573                                                mcd_buf_out = -1;
 574                                                continue;
 575                                        }
 576                                }
 577                                if (nr_sectors > CURRENT->nr_sectors)
 578                                        nr_sectors = CURRENT->nr_sectors;
 579                                memcpy(CURRENT->buffer, mcd_buf + offs,
 580                                       nr_sectors * 512);
 581                                CURRENT->nr_sectors -= nr_sectors;
 582                                CURRENT->sector += nr_sectors;
 583                                CURRENT->buffer += nr_sectors * 512;
 584                        } else {
 585                                mcd_buf_out = -1;
 586                                break;
 587                        }
 588                }
 589        }
 590}
 591
 592
 593/*
 594 * We only seem to get interrupts after an error.
 595 * Just take the interrupt and clear out the status reg.
 596 */
 597
 598static void mcd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 599{
 600        int st;
 601
 602        st = inb(MCDPORT(1)) & 0xFF;
 603        test1(printk("<int1-%02X>", st));
 604        if (!(st & MFL_STATUS)) {
 605                st = inb(MCDPORT(0)) & 0xFF;
 606                test1(printk("<int0-%02X>", st));
 607                if ((st & 0xFF) != 0xFF)
 608                        mcd_error = st ? st & 0xFF : -1;
 609        }
 610}
 611
 612
 613static void do_mcd_request(request_queue_t * q)
 614{
 615        test2(printk(" do_mcd_request(%ld+%ld)\n", CURRENT->sector,
 616               CURRENT->nr_sectors));
 617
 618                mcd_transfer_is_active = 1;
 619        while (CURRENT_VALID) {
 620                if (CURRENT->bh) {
 621                        if (!buffer_locked(CURRENT->bh))
 622                                panic(DEVICE_NAME ": block not locked");
 623                }
 624                mcd_transfer();
 625                if (CURRENT->nr_sectors == 0) {
 626                        end_request(1);
 627                } else {
 628                        mcd_buf_out = -1;       /* Want to read a block not in buffer */
 629                        if (mcd_state == MCD_S_IDLE) {
 630                                if (!tocUpToDate) {
 631                                        if (updateToc() < 0) {
 632                                                while (CURRENT_VALID)
 633                                                        end_request(0);
 634                                                break;
 635                                        }
 636                                }
 637                                mcd_state = MCD_S_START;
 638                                McdTries = 5;
 639                                mcd_timer.function = mcd_poll;
 640                                mod_timer(&mcd_timer, jiffies + 1);
 641                        }
 642                        break;
 643                }
 644        }
 645        mcd_transfer_is_active = 0;
 646        test2(printk(" do_mcd_request ends\n"));
 647}
 648
 649
 650
 651static void mcd_poll(unsigned long dummy)
 652{
 653        int st;
 654
 655
 656        if (mcd_error) {
 657                if (mcd_error & 0xA5) {
 658                        printk(KERN_ERR "mcd: I/O error 0x%02x", mcd_error);
 659                        if (mcd_error & 0x80)
 660                                printk(" (Door open)");
 661                        if (mcd_error & 0x20)
 662                                printk(" (Disk changed)");
 663                        if (mcd_error & 0x04) {
 664                                printk(" (Read error)");        /* Bitch about the problem. */
 665
 666                                /* Time to get fancy! If at 2x speed and 1 error, drop to 1x speed! */
 667                                /* Interesting how it STAYS at MCD_RETRY_ATTEMPTS on first error! */
 668                                /* But I find that rather HANDY!!! */
 669                                /* Neat! it REALLY WORKS on those LOW QUALITY CD's!!! Smile! :) */
 670                                /* AJK [06/17/95] */
 671
 672                                /* Slap the CD down to single speed! */
 673                                if (mcdDouble == 1
 674                                    && McdTries == MCD_RETRY_ATTEMPTS
 675                                    && MCMD_DATA_READ == MCMD_2X_READ) {
 676                                        MCMD_DATA_READ = MCMD_PLAY_READ;        /* Uhhh, Ummmm, muhuh-huh! */
 677                                        mcd1xhold = SINGLE_HOLD_SECTORS;        /* Hey Beavis! */
 678                                        printk(" Speed now 1x");        /* Pull my finger! */
 679                                }
 680                        }
 681                        printk("\n");
 682                        mcd_invalidate_buffers();
 683#ifdef WARN_IF_READ_FAILURE
 684                        if (McdTries == MCD_RETRY_ATTEMPTS)
 685                                printk(KERN_ERR "mcd: read of block %d failed\n",
 686                                       mcd_next_bn);
 687#endif
 688                        if (!McdTries--) {
 689                                /* Nuts! This cd is ready for recycling! */
 690                                /* When WAS the last time YOU cleaned it CORRECTLY?! */
 691                                printk(KERN_ERR "mcd: read of block %d failed, giving up\n",
 692                                     mcd_next_bn);
 693                                if (mcd_transfer_is_active) {
 694                                        McdTries = 0;
 695                                        goto ret;
 696                                }
 697                                if (CURRENT_VALID)
 698                                        end_request(0);
 699                                McdTries = MCD_RETRY_ATTEMPTS;
 700                        }
 701                }
 702                mcd_error = 0;
 703                mcd_state = MCD_S_STOP;
 704        }
 705        /* Switch back to Double speed if enough GOOD sectors were read! */
 706
 707        /* Are we a double speed with a crappy CD?! */
 708        if (mcdDouble == 1 && McdTries == MCD_RETRY_ATTEMPTS
 709            && MCMD_DATA_READ == MCMD_PLAY_READ) {
 710                /* We ARE a double speed and we ARE bitching! */
 711                if (mcd1xhold == 0) {   /* Okay, Like are we STILL at single speed? *//* We need to switch back to double speed now... */
 712                        MCMD_DATA_READ = MCMD_2X_READ;  /* Uhhh... BACK You GO! */
 713                        printk(KERN_INFO "mcd: Switching back to 2X speed!\n"); /* Tell 'em! */
 714                } else
 715                        mcd1xhold--;    /* No?! Count down the good reads some more... */
 716                /* and try, try again! */
 717        }
 718
 719immediately:
 720        switch (mcd_state) {
 721        case MCD_S_IDLE:
 722                test3(printk("MCD_S_IDLE\n"));
 723                goto out;
 724
 725        case MCD_S_START:
 726                test3(printk("MCD_S_START\n"));
 727                outb(MCMD_GET_STATUS, MCDPORT(0));
 728                mcd_state = mcd_mode == 1 ? MCD_S_READ : MCD_S_MODE;
 729                McdTimeout = 3000;
 730                break;
 731
 732        case MCD_S_MODE:
 733                test3(printk("MCD_S_MODE\n"));
 734                if ((st = mcdStatus()) != -1) {
 735                        if (st & MST_DSK_CHG) {
 736                                mcdDiskChanged = 1;
 737                                tocUpToDate = 0;
 738                                mcd_invalidate_buffers();
 739                        }
 740
 741set_mode_immediately:
 742                        if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) {
 743                                mcdDiskChanged = 1;
 744                                tocUpToDate = 0;
 745                                if (mcd_transfer_is_active) {
 746                                        mcd_state = MCD_S_START;
 747                                        goto immediately;
 748                                }
 749                                printk(KERN_INFO);
 750                                printk((st & MST_DOOR_OPEN) ?
 751                                       "mcd: door open\n" :
 752                                       "mcd: disk removed\n");
 753                                mcd_state = MCD_S_IDLE;
 754                                while (CURRENT_VALID)
 755                                        end_request(0);
 756                                goto out;
 757                        }
 758                        outb(MCMD_SET_MODE, MCDPORT(0));
 759                        outb(1, MCDPORT(0));
 760                        mcd_mode = 1;
 761                        mcd_state = MCD_S_READ;
 762                        McdTimeout = 3000;
 763                }
 764                break;
 765
 766        case MCD_S_READ:
 767                test3(printk("MCD_S_READ\n"));
 768                if ((st = mcdStatus()) != -1) {
 769                        if (st & MST_DSK_CHG) {
 770                                mcdDiskChanged = 1;
 771                                tocUpToDate = 0;
 772                                mcd_invalidate_buffers();
 773                        }
 774
 775read_immediately:
 776                        if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) {
 777                                mcdDiskChanged = 1;
 778                                tocUpToDate = 0;
 779                                if (mcd_transfer_is_active) {
 780                                        mcd_state = MCD_S_START;
 781                                        goto immediately;
 782                                }
 783                                printk(KERN_INFO);
 784                                printk((st & MST_DOOR_OPEN) ?
 785                                       "mcd: door open\n" :
 786                                       "mcd: disk removed\n");
 787                                mcd_state = MCD_S_IDLE;
 788                                while (CURRENT_VALID)
 789                                        end_request(0);
 790                                goto out;
 791                        }
 792
 793                        if (CURRENT_VALID) {
 794                                struct mcd_Play_msf msf;
 795                                mcd_next_bn = CURRENT->sector / 4;
 796                                hsg2msf(mcd_next_bn, &msf.start);
 797                                msf.end.min = ~0;
 798                                msf.end.sec = ~0;
 799                                msf.end.frame = ~0;
 800                                sendMcdCmd(MCMD_DATA_READ, &msf);
 801                                mcd_state = MCD_S_DATA;
 802                                McdTimeout = READ_TIMEOUT;
 803                        } else {
 804                                mcd_state = MCD_S_STOP;
 805                                goto immediately;
 806                        }
 807
 808                }
 809                break;
 810
 811        case MCD_S_DATA:
 812                test3(printk("MCD_S_DATA\n"));
 813                st = inb(MCDPORT(1)) & (MFL_STATUSorDATA);
 814data_immediately:
 815                test5(printk("Status %02x\n", st))
 816                switch (st) {
 817                case MFL_DATA:
 818#ifdef WARN_IF_READ_FAILURE
 819                        if (McdTries == 5)
 820                                printk(KERN_WARNING "mcd: read of block %d failed\n",
 821                                       mcd_next_bn);
 822#endif
 823                        if (!McdTries--) {
 824                                printk(KERN_ERR "mcd: read of block %d failed, giving up\n", mcd_next_bn);
 825                                if (mcd_transfer_is_active) {
 826                                        McdTries = 0;
 827                                        break;
 828                                }
 829                                if (CURRENT_VALID)
 830                                        end_request(0);
 831                                McdTries = 5;
 832                        }
 833                        mcd_state = MCD_S_START;
 834                        McdTimeout = READ_TIMEOUT;
 835                        goto immediately;
 836
 837                case MFL_STATUSorDATA:
 838                        break;
 839
 840                default:
 841                        McdTries = 5;
 842                        if (!CURRENT_VALID && mcd_buf_in == mcd_buf_out) {
 843                                mcd_state = MCD_S_STOP;
 844                                goto immediately;
 845                        }
 846                        mcd_buf_bn[mcd_buf_in] = -1;
 847                        insb(MCDPORT(0), mcd_buf + 2048 * mcd_buf_in,
 848                                  2048);
 849                        mcd_buf_bn[mcd_buf_in] = mcd_next_bn++;
 850                        if (mcd_buf_out == -1)
 851                                mcd_buf_out = mcd_buf_in;
 852                        mcd_buf_in = mcd_buf_in + 1 == MCD_BUF_SIZ ? 0 : mcd_buf_in + 1;
 853                        if (!mcd_transfer_is_active) {
 854                                while (CURRENT_VALID) {
 855                                        mcd_transfer();
 856                                        if (CURRENT->nr_sectors == 0)
 857                                                end_request(1);
 858                                        else
 859                                                break;
 860                                }
 861                        }
 862
 863                        if (CURRENT_VALID
 864                            && (CURRENT->sector / 4 < mcd_next_bn ||
 865                                CURRENT->sector / 4 > mcd_next_bn + 16)) {
 866                                mcd_state = MCD_S_STOP;
 867                                goto immediately;
 868                        }
 869                        McdTimeout = READ_TIMEOUT;
 870                        {
 871                                int count = QUICK_LOOP_COUNT;
 872                                while (count--) {
 873                                        QUICK_LOOP_DELAY;
 874                                        if ((st = (inb(MCDPORT(1))) & (MFL_STATUSorDATA)) != (MFL_STATUSorDATA)) {
 875                                                test4(printk(" %d ", QUICK_LOOP_COUNT - count));
 876                                                goto data_immediately;
 877                                        }
 878                                }
 879                                test4(printk("ended "));
 880                        }
 881                        break;
 882                }
 883                break;
 884
 885        case MCD_S_STOP:
 886                test3(printk("MCD_S_STOP\n"));
 887                if (!mitsumi_bug_93_wait)
 888                        goto do_not_work_around_mitsumi_bug_93_1;
 889
 890                McdTimeout = mitsumi_bug_93_wait;
 891                mcd_state = 9 + 3 + 1;
 892                break;
 893
 894        case 9 + 3 + 1:
 895                if (McdTimeout)
 896                        break;
 897
 898do_not_work_around_mitsumi_bug_93_1:
 899                outb(MCMD_STOP, MCDPORT(0));
 900                if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) {
 901                        int i = 4096;
 902                        do {
 903                                inb(MCDPORT(0));
 904                        } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i);
 905                        outb(MCMD_STOP, MCDPORT(0));
 906                        if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) {
 907                                i = 4096;
 908                                do {
 909                                        inb(MCDPORT(0));
 910                                } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i);
 911                                outb(MCMD_STOP, MCDPORT(0));
 912                        }
 913                }
 914
 915                mcd_state = MCD_S_STOPPING;
 916                McdTimeout = 1000;
 917                break;
 918
 919        case MCD_S_STOPPING:
 920                test3(printk("MCD_S_STOPPING\n"));
 921                if ((st = mcdStatus()) == -1 && McdTimeout)
 922                        break;
 923
 924                if ((st != -1) && (st & MST_DSK_CHG)) {
 925                        mcdDiskChanged = 1;
 926                        tocUpToDate = 0;
 927                        mcd_invalidate_buffers();
 928                }
 929                if (!mitsumi_bug_93_wait)
 930                        goto do_not_work_around_mitsumi_bug_93_2;
 931
 932                McdTimeout = mitsumi_bug_93_wait;
 933                mcd_state = 9 + 3 + 2;
 934                break;
 935
 936        case 9 + 3 + 2:
 937                if (McdTimeout)
 938                        break;
 939                st = -1;
 940
 941do_not_work_around_mitsumi_bug_93_2:
 942                test3(printk("CURRENT_VALID %d mcd_mode %d\n", CURRENT_VALID, mcd_mode));
 943                if (CURRENT_VALID) {
 944                        if (st != -1) {
 945                                if (mcd_mode == 1)
 946                                        goto read_immediately;
 947                                else
 948                                        goto set_mode_immediately;
 949                        } else {
 950                                mcd_state = MCD_S_START;
 951                                McdTimeout = 1;
 952                        }
 953                } else {
 954                        mcd_state = MCD_S_IDLE;
 955                        goto out;
 956                }
 957                break;
 958        default:
 959                printk(KERN_ERR "mcd: invalid state %d\n", mcd_state);
 960                goto out;
 961        }
 962ret:
 963        if (!McdTimeout--) {
 964                printk(KERN_WARNING "mcd: timeout in state %d\n", mcd_state);
 965                mcd_state = MCD_S_STOP;
 966        }
 967        mcd_timer.function = mcd_poll;
 968        mod_timer(&mcd_timer, jiffies + 1);
 969out:
 970        return;
 971}
 972
 973static void mcd_invalidate_buffers(void)
 974{
 975        int i;
 976        for (i = 0; i < MCD_BUF_SIZ; ++i)
 977                mcd_buf_bn[i] = -1;
 978        mcd_buf_out = -1;
 979}
 980
 981/*
 982 * Open the device special file.  Check that a disk is in.
 983 */
 984static int mcd_open(struct cdrom_device_info *cdi, int purpose)
 985{
 986        int st, count = 0;
 987        if (mcdPresent == 0)
 988                return -ENXIO;  /* no hardware */
 989
 990        if (mcd_open_count || mcd_state != MCD_S_IDLE)
 991                goto bump_count;
 992
 993        mcd_invalidate_buffers();
 994        do {
 995                st = statusCmd();       /* check drive status */
 996                if (st == -1)
 997                        goto err_out;   /* drive doesn't respond */
 998                if ((st & MST_READY) == 0) {    /* no disk? wait a sec... */
 999                        current->state = TASK_INTERRUPTIBLE;
1000                        schedule_timeout(HZ);
1001                }
1002        } while (((st & MST_READY) == 0) && count++ < MCD_RETRY_ATTEMPTS);
1003
1004        if (updateToc() < 0)
1005                goto err_out;
1006
1007bump_count:
1008        ++mcd_open_count;
1009        return 0;
1010
1011err_out:
1012        return -EIO;
1013}
1014
1015
1016/*
1017 * On close, we flush all mcd blocks from the buffer cache.
1018 */
1019static void mcd_release(struct cdrom_device_info *cdi)
1020{
1021        if (!--mcd_open_count) {
1022                mcd_invalidate_buffers();
1023        }
1024}
1025
1026
1027
1028/* This routine gets called during initialization if things go wrong,
1029 * and is used in mcd_exit as well. */
1030static void cleanup(int level)
1031{
1032        switch (level) {
1033        case 3:
1034                if (unregister_cdrom(&mcd_info)) {
1035                        printk(KERN_WARNING "Can't unregister cdrom mcd\n");
1036                        return;
1037                }
1038                free_irq(mcd_irq, NULL);
1039        case 2:
1040                release_region(mcd_port, 4);
1041        case 1:
1042                if (devfs_unregister_blkdev(MAJOR_NR, "mcd")) {
1043                        printk(KERN_WARNING "Can't unregister major mcd\n");
1044                        return;
1045                }
1046                blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
1047        default:;
1048        }
1049}
1050
1051
1052
1053/*
1054 * Test for presence of drive and initialize it.  Called at boot time.
1055 */
1056
1057int __init mcd_init(void)
1058{
1059        int count;
1060        unsigned char result[3];
1061        char msg[80];
1062
1063        if (mcd_port <= 0 || mcd_irq <= 0) {
1064                printk(KERN_INFO "mcd: not probing.\n");
1065                return -EIO;
1066        }
1067
1068        if (devfs_register_blkdev(MAJOR_NR, "mcd", &mcd_bdops) != 0) {
1069                printk(KERN_ERR "mcd: Unable to get major %d for Mitsumi CD-ROM\n", MAJOR_NR);
1070                return -EIO;
1071        }
1072        if (check_region(mcd_port, 4)) {
1073                cleanup(1);
1074                printk(KERN_ERR "mcd: Initialization failed, I/O port (%X) already in use\n", mcd_port);
1075                return -EIO;
1076        }
1077
1078        blksize_size[MAJOR_NR] = mcd_blocksizes;
1079        blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
1080        read_ahead[MAJOR_NR] = 4;
1081
1082        /* check for card */
1083
1084        outb(0, MCDPORT(1));    /* send reset */
1085        for (count = 0; count < 2000000; count++)
1086                (void) inb(MCDPORT(1)); /* delay a bit */
1087
1088        outb(0x40, MCDPORT(0)); /* send get-stat cmd */
1089        for (count = 0; count < 2000000; count++)
1090                if (!(inb(MCDPORT(1)) & MFL_STATUS))
1091                        break;
1092
1093        if (count >= 2000000) {
1094                printk(KERN_INFO "mcd: initialisation failed - No mcd device at 0x%x irq %d\n",
1095                       mcd_port, mcd_irq);
1096                cleanup(1);
1097                return -EIO;
1098        }
1099        count = inb(MCDPORT(0));        /* pick up the status */
1100
1101        outb(MCMD_GET_VERSION, MCDPORT(0));
1102        for (count = 0; count < 3; count++)
1103                if (getValue(result + count)) {
1104                        printk(KERN_ERR "mcd: mitsumi get version failed at 0x%x\n",
1105                               mcd_port);
1106                        cleanup(1);
1107                        return -EIO;
1108                }
1109
1110        if (result[0] == result[1] && result[1] == result[2]) {
1111                cleanup(1);
1112                return -EIO;
1113        }
1114
1115        mcdVersion = result[2];
1116
1117        if (mcdVersion >= 4)
1118                outb(4, MCDPORT(2));    /* magic happens */
1119
1120        /* don't get the IRQ until we know for sure the drive is there */
1121
1122        if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD", NULL)) {
1123                printk(KERN_ERR "mcd: Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
1124                cleanup(1);
1125                return -EIO;
1126        }
1127
1128        if (result[1] == 'D') {
1129                MCMD_DATA_READ = MCMD_2X_READ;
1130                /* Added flag to drop to 1x speed if too many errors */
1131                mcdDouble = 1;
1132        } else
1133                mcd_info.speed = 1;
1134        sprintf(msg, " mcd: Mitsumi %s Speed CD-ROM at port=0x%x,"
1135                " irq=%d\n", mcd_info.speed == 1 ? "Single" : "Double",
1136                mcd_port, mcd_irq);
1137
1138        request_region(mcd_port, 4, "mcd");
1139
1140        outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
1141        outb(0x02, MCDPORT(0));
1142        outb(0x00, MCDPORT(0));
1143        getValue(result);
1144
1145        outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
1146        outb(0x10, MCDPORT(0));
1147        outb(0x04, MCDPORT(0));
1148        getValue(result);
1149
1150        mcd_invalidate_buffers();
1151        mcdPresent = 1;
1152
1153        mcd_info.dev = MKDEV(MAJOR_NR, 0);
1154
1155        if (register_cdrom(&mcd_info) != 0) {
1156                printk(KERN_ERR "mcd: Unable to register Mitsumi CD-ROM.\n");
1157                cleanup(3);
1158                return -EIO;
1159        }
1160        devfs_plain_cdrom(&mcd_info, &mcd_bdops);
1161        printk(msg);
1162
1163        return 0;
1164}
1165
1166
1167static void hsg2msf(long hsg, struct msf *msf)
1168{
1169        hsg += 150;
1170        msf->min = hsg / 4500;
1171        hsg %= 4500;
1172        msf->sec = hsg / 75;
1173        msf->frame = hsg % 75;
1174
1175        bin2bcd(&msf->min);     /* convert to BCD */
1176        bin2bcd(&msf->sec);
1177        bin2bcd(&msf->frame);
1178}
1179
1180
1181static void bin2bcd(unsigned char *p)
1182{
1183        int u, t;
1184
1185        u = *p % 10;
1186        t = *p / 10;
1187        *p = u | (t << 4);
1188}
1189
1190static int bcd2bin(unsigned char bcd)
1191{
1192        return (bcd >> 4) * 10 + (bcd & 0xF);
1193}
1194
1195
1196/*
1197 * See if a status is ready from the drive and return it
1198 * if it is ready.
1199 */
1200
1201static int mcdStatus(void)
1202{
1203        int i;
1204        int st;
1205
1206        st = inb(MCDPORT(1)) & MFL_STATUS;
1207        if (!st) {
1208                i = inb(MCDPORT(0)) & 0xFF;
1209                return i;
1210        } else
1211                return -1;
1212}
1213
1214
1215/*
1216 * Send a play or read command to the drive
1217 */
1218
1219static void sendMcdCmd(int cmd, struct mcd_Play_msf *params)
1220{
1221        outb(cmd, MCDPORT(0));
1222        outb(params->start.min, MCDPORT(0));
1223        outb(params->start.sec, MCDPORT(0));
1224        outb(params->start.frame, MCDPORT(0));
1225        outb(params->end.min, MCDPORT(0));
1226        outb(params->end.sec, MCDPORT(0));
1227        outb(params->end.frame, MCDPORT(0));
1228}
1229
1230
1231/*
1232 * Timer interrupt routine to test for status ready from the drive.
1233 * (see the next routine)
1234 */
1235
1236static void mcdStatTimer(unsigned long dummy)
1237{
1238        if (!(inb(MCDPORT(1)) & MFL_STATUS)) {
1239                wake_up(&mcd_waitq);
1240                return;
1241        }
1242
1243        McdTimeout--;
1244        if (McdTimeout <= 0) {
1245                wake_up(&mcd_waitq);
1246                return;
1247        }
1248        mcd_timer.function = mcdStatTimer;
1249        mod_timer(&mcd_timer, jiffies + 1);
1250}
1251
1252
1253/*
1254 * Wait for a status to be returned from the drive.  The actual test
1255 * (see routine above) is done by the timer interrupt to avoid
1256 * excessive rescheduling.
1257 */
1258
1259static int getMcdStatus(int timeout)
1260{
1261        int st;
1262
1263        McdTimeout = timeout;
1264        mcd_timer.function = mcdStatTimer;
1265        mod_timer(&mcd_timer, jiffies + 1);
1266        sleep_on(&mcd_waitq);
1267        if (McdTimeout <= 0)
1268                return -1;
1269
1270        st = inb(MCDPORT(0)) & 0xFF;
1271        if (st == 0xFF)
1272                return -1;
1273
1274        if ((st & MST_BUSY) == 0 && audioStatus == CDROM_AUDIO_PLAY)
1275                /* XXX might be an error? look at q-channel? */
1276                audioStatus = CDROM_AUDIO_COMPLETED;
1277
1278        if (st & MST_DSK_CHG) {
1279                mcdDiskChanged = 1;
1280                tocUpToDate = 0;
1281                audioStatus = CDROM_AUDIO_NO_STATUS;
1282        }
1283
1284        return st;
1285}
1286
1287
1288/* gives current state of the drive This function is quite unreliable, 
1289   and should probably be rewritten by someone, eventually... */
1290
1291int mcd_drive_status(struct cdrom_device_info *cdi, int slot_nr)
1292{
1293        int st;
1294
1295        st = statusCmd();       /* check drive status */
1296        if (st == -1)
1297                return -EIO;    /* drive doesn't respond */
1298        if ((st & MST_READY))
1299                return CDS_DISC_OK;
1300        if ((st & MST_DOOR_OPEN))
1301                return CDS_TRAY_OPEN;
1302        if ((st & MST_DSK_CHG))
1303                return CDS_NO_DISC;
1304        if ((st & MST_BUSY))
1305                return CDS_DRIVE_NOT_READY;
1306        return -EIO;
1307}
1308
1309
1310/*
1311 * Read a value from the drive.
1312 */
1313
1314static int getValue(unsigned char *result)
1315{
1316        int count;
1317        int s;
1318
1319        for (count = 0; count < 2000; count++)
1320                if (!(inb(MCDPORT(1)) & MFL_STATUS))
1321                        break;
1322
1323        if (count >= 2000) {
1324                printk("mcd: getValue timeout\n");
1325                return -1;
1326        }
1327
1328        s = inb(MCDPORT(0)) & 0xFF;
1329        *result = (unsigned char) s;
1330        return 0;
1331}
1332
1333/*
1334 * Read the current Q-channel info.  Also used for reading the
1335 * table of contents.
1336 */
1337
1338int GetQChannelInfo(struct mcd_Toc *qp)
1339{
1340        unsigned char notUsed;
1341        int retry;
1342
1343        for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) {
1344                outb(MCMD_GET_Q_CHANNEL, MCDPORT(0));
1345                if (getMcdStatus(MCD_STATUS_DELAY) != -1)
1346                        break;
1347        }
1348
1349        if (retry >= MCD_RETRY_ATTEMPTS)
1350                return -1;
1351
1352        if (getValue(&qp->ctrl_addr) < 0)
1353                return -1;
1354        if (getValue(&qp->track) < 0)
1355                return -1;
1356        if (getValue(&qp->pointIndex) < 0)
1357                return -1;
1358        if (getValue(&qp->trackTime.min) < 0)
1359                return -1;
1360        if (getValue(&qp->trackTime.sec) < 0)
1361                return -1;
1362        if (getValue(&qp->trackTime.frame) < 0)
1363                return -1;
1364        if (getValue(&notUsed) < 0)
1365                return -1;
1366        if (getValue(&qp->diskTime.min) < 0)
1367                return -1;
1368        if (getValue(&qp->diskTime.sec) < 0)
1369                return -1;
1370        if (getValue(&qp->diskTime.frame) < 0)
1371                return -1;
1372
1373        return 0;
1374}
1375
1376/*
1377 * Read the table of contents (TOC) and TOC header if necessary
1378 */
1379
1380static int updateToc(void)
1381{
1382        if (tocUpToDate)
1383                return 0;
1384
1385        if (GetDiskInfo() < 0)
1386                return -EIO;
1387
1388        if (GetToc() < 0)
1389                return -EIO;
1390
1391        tocUpToDate = 1;
1392        return 0;
1393}
1394
1395/*
1396 * Read the table of contents header
1397 */
1398
1399static int GetDiskInfo(void)
1400{
1401        int retry;
1402
1403        for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) {
1404                outb(MCMD_GET_DISK_INFO, MCDPORT(0));
1405                if (getMcdStatus(MCD_STATUS_DELAY) != -1)
1406                        break;
1407        }
1408
1409        if (retry >= MCD_RETRY_ATTEMPTS)
1410                return -1;
1411
1412        if (getValue(&DiskInfo.first) < 0)
1413                return -1;
1414        if (getValue(&DiskInfo.last) < 0)
1415                return -1;
1416
1417        DiskInfo.first = bcd2bin(DiskInfo.first);
1418        DiskInfo.last = bcd2bin(DiskInfo.last);
1419
1420#ifdef MCD_DEBUG
1421        printk
1422            ("Disk Info: first %d last %d length %02x:%02x.%02x first %02x:%02x.%02x\n",
1423             DiskInfo.first, DiskInfo.last, DiskInfo.diskLength.min,
1424             DiskInfo.diskLength.sec, DiskInfo.diskLength.frame,
1425             DiskInfo.firstTrack.min, DiskInfo.firstTrack.sec,
1426             DiskInfo.firstTrack.frame);
1427#endif
1428
1429        if (getValue(&DiskInfo.diskLength.min) < 0)
1430                return -1;
1431        if (getValue(&DiskInfo.diskLength.sec) < 0)
1432                return -1;
1433        if (getValue(&DiskInfo.diskLength.frame) < 0)
1434                return -1;
1435        if (getValue(&DiskInfo.firstTrack.min) < 0)
1436                return -1;
1437        if (getValue(&DiskInfo.firstTrack.sec) < 0)
1438                return -1;
1439        if (getValue(&DiskInfo.firstTrack.frame) < 0)
1440                return -1;
1441
1442        return 0;
1443}
1444
1445/*
1446 * Read the table of contents (TOC)
1447 */
1448
1449static int GetToc(void)
1450{
1451        int i, px;
1452        int limit;
1453        int retry;
1454        struct mcd_Toc qInfo;
1455
1456        for (i = 0; i < MAX_TRACKS; i++)
1457                Toc[i].pointIndex = 0;
1458
1459        i = DiskInfo.last + 3;
1460
1461        for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) {
1462                outb(MCMD_STOP, MCDPORT(0));
1463                if (getMcdStatus(MCD_STATUS_DELAY) != -1)
1464                        break;
1465        }
1466
1467        if (retry >= MCD_RETRY_ATTEMPTS)
1468                return -1;
1469
1470        for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) {
1471                outb(MCMD_SET_MODE, MCDPORT(0));
1472                outb(0x05, MCDPORT(0)); /* mode: toc */
1473                mcd_mode = 0x05;
1474                if (getMcdStatus(MCD_STATUS_DELAY) != -1)
1475                        break;
1476        }
1477
1478        if (retry >= MCD_RETRY_ATTEMPTS)
1479                return -1;
1480
1481        for (limit = 300; limit > 0; limit--) {
1482                if (GetQChannelInfo(&qInfo) < 0)
1483                        break;
1484
1485                px = bcd2bin(qInfo.pointIndex);
1486                if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
1487                        if (Toc[px].pointIndex == 0) {
1488                                Toc[px] = qInfo;
1489                                i--;
1490                        }
1491
1492                if (i <= 0)
1493                        break;
1494        }
1495
1496        Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
1497
1498        for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) {
1499                outb(MCMD_SET_MODE, MCDPORT(0));
1500                outb(0x01, MCDPORT(0));
1501                mcd_mode = 1;
1502                if (getMcdStatus(MCD_STATUS_DELAY) != -1)
1503                        break;
1504        }
1505
1506#ifdef MCD_DEBUG
1507        for (i = 1; i <= DiskInfo.last; i++)
1508                printk
1509                    ("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X    %02X:%02X.%02X\n",
1510                     i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
1511                     Toc[i].trackTime.min, Toc[i].trackTime.sec,
1512                     Toc[i].trackTime.frame, Toc[i].diskTime.min,
1513                     Toc[i].diskTime.sec, Toc[i].diskTime.frame);
1514        for (i = 100; i < 103; i++)
1515                printk
1516                    ("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X    %02X:%02X.%02X\n",
1517                     i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
1518                     Toc[i].trackTime.min, Toc[i].trackTime.sec,
1519                     Toc[i].trackTime.frame, Toc[i].diskTime.min,
1520                     Toc[i].diskTime.sec, Toc[i].diskTime.frame);
1521#endif
1522
1523        return limit > 0 ? 0 : -1;
1524}
1525
1526void __exit mcd_exit(void)
1527{
1528        cleanup(3);
1529        del_timer_sync(&mcd_timer);
1530}
1531
1532#ifdef MODULE
1533module_init(mcd_init);
1534#endif
1535module_exit(mcd_exit);
1536
1537MODULE_AUTHOR("Martin Harriss");
1538MODULE_LICENSE("GPL");
1539
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.