root/drivers/cdrom/mcd.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mcd_setup
  2. check_mcd_change
  3. statusCmd
  4. mcdPlay
  5. msf2hsg
  6. mcd_ioctl
  7. mcd_transfer
  8. mcd_interrupt
  9. do_mcd_request
  10. mcd_poll
  11. mcd_invalidate_buffers
  12. mcd_open
  13. mcd_release
  14. mcd_init
  15. hsg2msf
  16. bin2bcd
  17. bcd2bin
  18. mcdStatus
  19. sendMcdCmd
  20. mcdStatTimer
  21. getMcdStatus
  22. getValue
  23. GetQChannelInfo
  24. updateToc
  25. GetDiskInfo
  26. GetToc
  27. init_module
  28. cleanup_module

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

/* [previous][next][first][last][top][bottom][index][help] */