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

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