root/drivers/block/optcd.c

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

DEFINITIONS

This source file includes following definitions.
  1. optFlags
  2. sten_low
  3. dten_low
  4. sleep_timer
  5. sleep_status
  6. sleep_sten_low
  7. sleep_dten_low
  8. optSendCmd
  9. optSendParams
  10. optGetExecStatus
  11. optSleepTillExecStatus
  12. optStatus
  13. optGetData
  14. optReadData
  15. optFlushData
  16. optResetDrive
  17. optCmd
  18. optPlayCmd
  19. optReadCmd
  20. bin2bcd
  21. hsg2msf
  22. bcd2bin
  23. msf2hsg
  24. optGetStatus
  25. optGetQChannelInfo
  26. optGetDiskInfo
  27. optGetToc
  28. optUpdateToc
  29. opt_invalidate_buffers
  30. opt_transfer
  31. opt_poll
  32. do_optcd_request
  33. opt_ioctl
  34. opt_open
  35. opt_release
  36. version_ok
  37. optcd_setup
  38. optcd_init
  39. init_module
  40. cleanup_module

   1 /*      $Id: optcd.c,v 1.7 1995/06/28 20:20:13 root Exp $
   2         linux/drivers/block/optcd.c - Optics Storage 8000 AT CDROM driver
   3 
   4         Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl)
   5 
   6         Based on Aztech CD268 CDROM driver by Werner Zimmermann and preworks
   7         by Eberhard Moenkeberg.
   8 
   9         This program is free software; you can redistribute it and/or modify
  10         it under the terms of the GNU General Public License as published by
  11         the Free Software Foundation; either version 2, or (at your option)
  12         any later version.
  13 
  14         This program is distributed in the hope that it will be useful,
  15         but WITHOUT ANY WARRANTY; without even the implied warranty of
  16         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17         GNU General Public License for more details.
  18 
  19         You should have received a copy of the GNU General Public License
  20         along with this program; if not, write to the Free Software
  21         Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22 
  23         History
  24         14-5-95         v0.0    Plays sound tracks. No reading of data CDs yet.
  25                                 Detection of disk change doesn't work.
  26         21-5-95         v0.1    First ALPHA version. CD can be mounted. The
  27                                 device major nr is borrowed from the Aztech
  28                                 driver. Speed is around 240 kb/s, as measured
  29                                 with "time dd if=/dev/cdrom of=/dev/null \
  30                                 bs=2048 count=4096".
  31         24-6-95         v0.2    Reworked the #defines for the command codes
  32                                 and the like, as well as the structure of
  33                                 the hardware communication protocol, to
  34                                 reflect the "official" documentation, kindly
  35                                 supplied by C.K. Tan, Optics Storage Pte. Ltd.
  36                                 Also tidied up the state machine somewhat.
  37         28-6-95         v0.3    Removed the ISP-16 interface code, as this
  38                                 should go into its own driver. The driver now
  39                                 has its own major nr.
  40                                 Disk change detection now seems to work, too.
  41 */
  42 
  43 #include <linux/major.h>
  44 #include <linux/config.h>
  45 
  46 #ifdef MODULE
  47 # include <linux/module.h>
  48 # include <linux/version.h>
  49 # ifndef CONFIG_MODVERSIONS
  50         char kernel_version[]= UTS_RELEASE;
  51 # endif
  52 #else
  53 # define MOD_INC_USE_COUNT
  54 # define MOD_DEC_USE_COUNT
  55 #endif
  56 
  57 #include <linux/errno.h>
  58 #include <linux/mm.h>
  59 #include <linux/fs.h>
  60 #include <linux/timer.h>
  61 #include <linux/kernel.h>
  62 #include <linux/cdrom.h>
  63 #include <linux/ioport.h>
  64 #include <asm/io.h>
  65 
  66 #define MAJOR_NR OPTICS_CDROM_MAJOR
  67 
  68 # include "blk.h"
  69 #define optcd_port optcd        /* Needed for the modutils. */
  70 # include <linux/optcd.h>
  71 
  72 static short optcd_port = OPTCD_PORTBASE;
  73 
  74 /* Read current status/data availability flags */
  75 inline static int optFlags(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
  76         return inb(STATUS_PORT) & FL_STDT;
  77 }
  78 
  79 /* Wait for status available; return TRUE on timeout */
  80 static int sten_low(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
  81         int no_status;
  82         unsigned long count = 0;
  83         while ((no_status = (optFlags() & FL_STEN)))
  84                 if (++count >= BUSY_TIMEOUT)
  85                         break;
  86 #ifdef DEBUG_DRIVE_IF
  87         if (no_status)
  88                 printk("optcd: timeout waiting for STEN low\n");
  89         else
  90                 printk("optcd: STEN low after %ld\n", count);
  91 #endif
  92         return no_status;
  93 }
  94 
  95 /* Wait for data available; return TRUE on timeout */
  96 static int dten_low(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
  97         int no_data;
  98         unsigned long count = 0;
  99         while ((no_data = (optFlags() & FL_DTEN)))
 100                 if (++count >= BUSY_TIMEOUT)
 101                         break;
 102 #ifdef DEBUG_DRIVE_IF
 103         if (no_data)
 104                 printk("optcd: timeout waiting for DTEN low\n");
 105         else
 106                 printk("optcd: DTEN low after %ld\n", count);
 107 #endif
 108         return no_data;
 109 }
 110 
 111 /* Facilities for polled waiting for status or data */
 112 static int sleep_timeout;               /* Max amount of time still to sleep */
 113 static unsigned char sleep_flags;       /* Flags read last time around */
 114 static struct wait_queue *waitq = NULL;
 115 static struct timer_list delay_timer = {NULL, NULL, 0, 0, NULL};
 116 
 117 /* Timer routine: wake up when either of FL_STEN or FL_DTEN goes down,
 118  * or when timeout expires. Otherwise wait some more.
 119  */
 120 static void sleep_timer(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 121         if ((sleep_flags = optFlags()) != FL_STDT) {
 122                 wake_up(&waitq);
 123                 return;
 124         }
 125         if (--sleep_timeout <= 0) {
 126                 wake_up(&waitq);
 127                 return;
 128         }
 129         SET_TIMER(sleep_timer, 1);
 130 }
 131 
 132 /* Sleep until any of FL_STEN or FL_DTEN go down, or until timeout.
 133  * sleep_timeout must be set first.
 134  */
 135 static int sleep_status(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 136 #ifdef DEBUG_DRIVE_IF
 137         printk("optcd: sleeping %d on status\n", sleep_timeout);
 138 #endif
 139         if (sleep_timeout <= 0)         /* timeout immediately */
 140                 return FL_STDT;
 141         if ((sleep_flags = optFlags()) == FL_STDT) {
 142                 SET_TIMER(sleep_timer, 1);
 143                 sleep_on(&waitq);
 144         }
 145 #ifdef DEBUG_DRIVE_IF
 146         printk("optcd: woken up with %d to go, flags %d\n",
 147                 sleep_timeout, sleep_flags);
 148 #endif
 149         return sleep_flags;
 150 }
 151 
 152 /* Sleep until status available; return TRUE on timeout */
 153 inline static int sleep_sten_low(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 154         int flags;
 155         sleep_timeout = SLEEP_TIMEOUT;
 156         flags = sleep_status();
 157 #ifdef DEBUG_DRIVE_IF
 158         if (!(flags & FL_DTEN))
 159                 printk("optcd: DTEN while waiting for STEN\n");
 160 #endif
 161         return flags & FL_STEN;
 162 }
 163 
 164 /* Sleep until data available; return TRUE on timeout */
 165 inline static int sleep_dten_low(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 166         int flags;
 167         sleep_timeout = SLEEP_TIMEOUT;
 168         flags = sleep_status();
 169 #ifdef DEBUG_DRIVE_IF
 170         if (!(flags & FL_STEN))
 171                 printk("optcd: STEN while waiting for DTEN\n");
 172 #endif
 173         return flags & FL_DTEN;
 174 }
 175 
 176 /* Send command code. Return <0 indicates error */
 177 static int optSendCmd(int cmd) {
     /* [previous][next][first][last][top][bottom][index][help] */
 178         unsigned char ack;
 179 #if defined(DEBUG_DRIVE_IF)||defined(DEBUG_COMMANDS)
 180         printk("optcd: executing command 0x%02x\n", cmd);
 181 #endif
 182         outb(HCON_DTS, HCON_PORT);      /* Enable Suspend Data Transfer */
 183         outb(cmd, COMIN_PORT);          /* Send command code */
 184         if (sten_low())                 /* Wait for status available */
 185                 return -ERR_IF_CMD_TIMEOUT;
 186         ack = inb(DATA_PORT);           /* read command acknowledge */
 187 #ifdef DEBUG_DRIVE_IF
 188         printk("optcd: acknowledge code 0x%02x\n", ack);
 189 #endif
 190         outb(HCON_SDRQB, HCON_PORT);    /* Disable Suspend Data Transfer */
 191         return ack==ST_OP_OK ? 0 : -ack;
 192 }
 193 
 194 /* Send command parameters. Return <0 indicates error */
 195 static int optSendParams(struct opt_Play_msf *params) {
     /* [previous][next][first][last][top][bottom][index][help] */
 196         unsigned char ack;
 197 #if defined(DEBUG_DRIVE_IF)||defined(DEBUG_COMMANDS)
 198         printk("optcd: params %02x:%02x:%02x %02x:%02x:%02x\n",
 199                 params->start.min, params->start.sec, params->start.frame,
 200                 params->end.min, params->end.sec, params->end.frame);
 201 #endif
 202         outb(params -> start.min, COMIN_PORT);
 203         outb(params -> start.sec, COMIN_PORT);
 204         outb(params -> start.frame, COMIN_PORT);
 205         outb(params -> end.min, COMIN_PORT);
 206         outb(params -> end.sec, COMIN_PORT);
 207         outb(params -> end.frame, COMIN_PORT);
 208         if (sten_low())                 /* Wait for status available */
 209                 return -ERR_IF_CMD_TIMEOUT;
 210         ack = inb(DATA_PORT);           /* read command acknowledge */
 211 #ifdef DEBUG_DRIVE_IF
 212         printk("optcd: acknowledge code 0x%02x\n", ack);
 213 #endif
 214         return ack==ST_PA_OK ? 0 : -ack;
 215 }
 216 
 217 /* Return execution status for quick response commands, i.e. busy wait.
 218  * Return value <0 indicates timeout.
 219  */
 220 static int optGetExecStatus(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 221         unsigned char exec_status;
 222         if (sten_low())                 /* Wait for status available */
 223                 return -ERR_IF_CMD_TIMEOUT;
 224         exec_status = inb(DATA_PORT);   /* read command execution status */
 225 #ifdef DEBUG_DRIVE_IF
 226         printk("optcd: returned execution status: 0x%02x\n", exec_status);
 227 #endif
 228         return exec_status;
 229 }
 230 
 231 /* Return execution status for slow commands. Only use when no data is
 232  * expected. Return value <0 indicates timeout.
 233  */
 234 static int optSleepTillExecStatus(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 235         unsigned char exec_status;
 236         if (sleep_sten_low())           /* Wait for status available */
 237                 return -ERR_IF_CMD_TIMEOUT;
 238         exec_status = inb(DATA_PORT);   /* read command execution status */
 239 #ifdef DEBUG_DRIVE_IF
 240         printk("optcd: returned execution status: 0x%02x\n", exec_status);
 241 #endif
 242         return exec_status;
 243 }
 244 
 245 /* Fetch status that has previously been waited for. <0 means not available */
 246 inline static int optStatus(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 247         unsigned char status;
 248         if (optFlags() & FL_STEN)
 249                 return -ERR_IF_NOSTAT;
 250         status = inb(DATA_PORT);
 251 #ifdef DEBUG_DRIVE_IF
 252         printk("optcd: read status: 0x%02x\n", status);
 253 #endif
 254         return status;
 255 }
 256 
 257 /* Wait for extra byte of data that a command returns */
 258 static int optGetData(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 259         unsigned char data;
 260         if (sten_low())
 261                 return -ERR_IF_DATA_TIMEOUT;
 262         data = inb(DATA_PORT);
 263 #ifdef DEBUG_DRIVE_IF
 264         printk("optcd: read data: 0x%02x\n", data);
 265 #endif
 266         return data;
 267 }
 268 
 269 /* Read data that has previously been waited for. */
 270 inline static void optReadData(char *buf, int n) {
     /* [previous][next][first][last][top][bottom][index][help] */
 271         insb(DATA_PORT, buf, n);
 272 }
 273 
 274 /* Flush status and data fifos */
 275 inline static void optFlushData(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 276         while (optFlags() != FL_STDT)
 277                 inb(DATA_PORT);
 278 }
 279 
 280 /* Write something to RESET_PORT and wait. Return TRUE upon success. */
 281 static int optResetDrive(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 282         unsigned long count = 0;
 283         int flags;
 284 #ifdef DEBUG_DRIVE_IF
 285         printk("optcd: reset drive\n");
 286 #endif
 287         outb(0, RESET_PORT);
 288         while (++count < RESET_WAIT)
 289                 inb(DATA_PORT);
 290         count = 0;
 291         while ((flags = (inb(STATUS_PORT) & FL_RESET)) != FL_RESET)
 292                 if (++count >= BUSY_TIMEOUT)
 293                         break;
 294 #ifdef DEBUG_DRIVE_IF
 295         if (flags == FL_RESET)
 296                 printk("optcd: drive reset\n");
 297         else
 298                 printk("optcd: reset failed\n");
 299 #endif
 300         if (flags != FL_RESET)
 301                 return 0;               /* Reset failed */
 302         outb(HCON_SDRQB, HCON_PORT);    /* Disable Suspend Data Transfer */
 303         return 1;                       /* Reset succeeded */
 304 }
 305 
 306 
 307 /* Command protocol */
 308 
 309 /* Send a simple command and wait for response */
 310 inline static int optCmd(int cmd) {
     /* [previous][next][first][last][top][bottom][index][help] */
 311         int ack = optSendCmd(cmd);
 312         if (ack < 0)
 313                 return ack;
 314         if (cmd < COMFETCH)             /* Quick response command */
 315                 return optGetExecStatus();
 316         else                            /* Slow command */
 317                 return optSleepTillExecStatus();
 318 }
 319 
 320 /* Send a command with parameters and wait for response */
 321 inline static int optPlayCmd(int cmd, struct opt_Play_msf *params) {
     /* [previous][next][first][last][top][bottom][index][help] */
 322         int ack = optSendCmd(cmd);
 323         if (ack < 0)
 324                 return ack;
 325         if ((ack = optSendParams(params)) < 0)
 326                 return ack;
 327         return optSleepTillExecStatus();
 328 }
 329 
 330 /* Send a command with parameters. Don't wait for the response,
 331  * which consists of the data blocks read. */
 332 inline static int optReadCmd(int cmd, struct opt_Play_msf *params) {
     /* [previous][next][first][last][top][bottom][index][help] */
 333         int ack = optSendCmd(cmd);
 334         if (ack < 0)
 335                 return ack;
 336         return optSendParams(params);
 337 }
 338 
 339 
 340 /* Address conversion routines */
 341 
 342 /* Binary to BCD (2 digits) */
 343 inline static unsigned char bin2bcd(unsigned char p) {
     /* [previous][next][first][last][top][bottom][index][help] */
 344 #ifdef DEBUG_CONV
 345         if (p > 99)
 346                 printk("optcd: error bin2bcd %d\n", p);
 347 #endif
 348         return (p % 10) | ((p / 10) << 4);
 349 }
 350 
 351 /* Linear address to minute, second, frame form */
 352 static void hsg2msf(long hsg, struct msf *msf) {
     /* [previous][next][first][last][top][bottom][index][help] */
 353         hsg += 150;
 354         msf -> min = hsg / 4500;
 355         hsg %= 4500;
 356         msf -> sec = hsg / 75;
 357         msf -> frame = hsg % 75;
 358 #ifdef DEBUG_CONV
 359         if (msf -> min >= 70)
 360                 printk("optcd: Error hsg2msf address Minutes\n");
 361         if (msf -> sec >= 60)
 362                 printk("optcd: Error hsg2msf address Seconds\n");
 363         if (msf -> frame >= 75)
 364                 printk("optcd: Error hsg2msf address Frames\n");
 365 #endif
 366         msf -> min = bin2bcd(msf -> min);       /* convert to BCD */
 367         msf -> sec = bin2bcd(msf -> sec);
 368         msf -> frame = bin2bcd(msf -> frame);
 369 }
 370 
 371 /* Two BCD digits to binary */
 372 inline static int bcd2bin(unsigned char bcd) {
     /* [previous][next][first][last][top][bottom][index][help] */
 373         return (bcd >> 4) * 10 + (bcd & 0x0f);
 374 }
 375 
 376 /* Minute, second, frame address to linear address */
 377 static long msf2hsg(struct msf *mp) {
     /* [previous][next][first][last][top][bottom][index][help] */
 378 #ifdef DEBUG_CONV
 379         if (mp -> min >= 70)
 380                 printk("optcd: Error msf2hsg address Minutes\n");
 381         if (mp -> sec >= 60)
 382                 printk("optcd: Error msf2hsg address Seconds\n");
 383         if (mp -> frame >= 75)
 384                 printk("optcd: Error msf2hsg address Frames\n");
 385 #endif
 386         return bcd2bin(mp -> frame)
 387                 + bcd2bin(mp -> sec) * 75
 388                 + bcd2bin(mp -> min) * 4500
 389                 - 150;
 390 }
 391 
 392 
 393 /* Drive status and table of contents */
 394 
 395 static int optAudioStatus = CDROM_AUDIO_NO_STATUS;
 396 static char optDiskChanged = 1;
 397 static char optTocUpToDate = 0;
 398 static struct opt_DiskInfo DiskInfo;
 399 static struct opt_Toc Toc[MAX_TRACKS];
 400 
 401 /* Get CDROM status, flagging completion of audio play and disk changes. */
 402 static int optGetStatus(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 403         int st;
 404         if ((st = optCmd(COMIOCTLISTAT)) < 0)
 405                 return st;
 406         if (st == 0xff)
 407                 return -ERR_IF_NOSTAT;
 408         if (((st & ST_MODE_BITS) != ST_M_AUDIO) &&
 409                 (optAudioStatus == CDROM_AUDIO_PLAY)) {
 410                 optAudioStatus = CDROM_AUDIO_COMPLETED;
 411         }
 412         if (st & ST_DSK_CHG) {
 413                 optDiskChanged = 1;
 414                 optTocUpToDate = 0;
 415                 optAudioStatus = CDROM_AUDIO_NO_STATUS;
 416         }
 417         return st;
 418 }
 419 
 420 /*
 421  * Read the current Q-channel info. Also used for reading the
 422  * table of contents.
 423  */
 424 static int optGetQChannelInfo(struct opt_Toc *qp) {
     /* [previous][next][first][last][top][bottom][index][help] */
 425         int st;
 426 #ifdef DEBUG_TOC
 427         printk("optcd: starting optGetQChannelInfo\n");
 428 #endif
 429         if ((st = optGetStatus()) < 0)
 430                 return st;
 431         if ((st = optCmd(COMSUBQ)) < 0)
 432                 return st;
 433         if ((qp -> ctrl_addr = st = optGetData()), st < 0) return st;
 434         if ((qp -> track = st = optGetData()), st < 0) return st;
 435         if ((qp -> pointIndex = st = optGetData()), st < 0) return st;
 436         if ((qp -> trackTime.min = st = optGetData()), st < 0) return st;
 437         if ((qp -> trackTime.sec = st = optGetData()), st < 0) return st;
 438         if ((qp -> trackTime.frame = st = optGetData()), st < 0) return st;
 439         if ((st = optGetData()) < 0) return st;         /* byte not used */
 440         if ((qp -> diskTime.min = st = optGetData()), st < 0) return st;
 441         if ((qp -> diskTime.sec = st = optGetData()), st < 0) return st;
 442         if ((qp -> diskTime.frame = st = optGetData()), st < 0) return st;
 443 #ifdef DEBUG_TOC
 444         printk("optcd: exiting optGetQChannelInfo\n");
 445 #endif
 446         return 0;
 447 }
 448 
 449 #define QINFO_FIRSTTRACK        0xa0
 450 #define QINFO_LASTTRACK         0xa1
 451 #define QINFO_DISKLENGTH        0xa2
 452 
 453 static int optGetDiskInfo(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 454         int st, limit;
 455         unsigned char test = 0;
 456         struct opt_Toc qInfo;
 457 #ifdef DEBUG_TOC
 458         printk("optcd: starting optGetDiskInfo\n");
 459 #endif
 460         optDiskChanged = 0;
 461         if ((st = optCmd(COMLEADIN)) < 0)
 462                 return st;
 463         for (limit = 300; (limit > 0) && (test != 0x0f); limit--) {
 464                 if ((st = optGetQChannelInfo(&qInfo)) < 0)
 465                         return st;
 466                 switch (qInfo.pointIndex) {
 467                 case QINFO_FIRSTTRACK:
 468                         DiskInfo.first = bcd2bin(qInfo.diskTime.min);
 469 #ifdef DEBUG_TOC
 470                         printk("optcd: got first: %d\n", DiskInfo.first);
 471 #endif
 472                         test |= 0x01;
 473                         break;
 474                 case QINFO_LASTTRACK:
 475                         DiskInfo.last = bcd2bin(qInfo.diskTime.min);
 476 #ifdef DEBUG_TOC
 477                         printk("optcd: got last: %d\n", DiskInfo.last);
 478 #endif
 479                         test |= 0x02;
 480                         break;
 481                 case QINFO_DISKLENGTH:
 482                         DiskInfo.diskLength.min = qInfo.diskTime.min;
 483                         DiskInfo.diskLength.sec = qInfo.diskTime.sec-2;
 484                         DiskInfo.diskLength.frame = qInfo.diskTime.frame;
 485 #ifdef DEBUG_TOC
 486                         printk("optcd: got length: %x:%x.%x\n",
 487                                 DiskInfo.diskLength.min,
 488                                 DiskInfo.diskLength.sec,
 489                                 DiskInfo.diskLength.frame);
 490 #endif
 491                         test |= 0x04;
 492                         break;
 493                 default:
 494                         if ((test & 0x01)       /* Got no of first track */
 495                          && (qInfo.pointIndex == DiskInfo.first)) {
 496                                 /* StartTime of First Track */
 497                                 DiskInfo.firstTrack.min = qInfo.diskTime.min;
 498                                 DiskInfo.firstTrack.sec = qInfo.diskTime.sec;
 499                                 DiskInfo.firstTrack.frame = qInfo.diskTime.frame;
 500 #ifdef DEBUG_TOC
 501                         printk("optcd: got start: %x:%x.%x\n",
 502                                 DiskInfo.firstTrack.min,
 503                                 DiskInfo.firstTrack.sec,
 504                                 DiskInfo.firstTrack.frame);
 505 #endif
 506                                 test |= 0x08;
 507                         }
 508                 }
 509         }
 510 #ifdef DEBUG_TOC
 511         printk("optcd: exiting optGetDiskInfo\n");
 512 #endif
 513         if (test != 0x0f)
 514                 return -ERR_TOC_MISSINGINFO;
 515         return 0;
 516 }
 517 
 518 static int optGetToc(void) {    /* Presumes we have got DiskInfo */
     /* [previous][next][first][last][top][bottom][index][help] */
 519         int st, count, px, limit;
 520         struct opt_Toc qInfo;
 521 #ifdef DEBUG_TOC
 522         int i;
 523         printk("optcd: starting optGetToc\n");
 524 #endif
 525         for (count = 0; count < MAX_TRACKS; count++)
 526                 Toc[count].pointIndex = 0;
 527         if ((st = optCmd(COMLEADIN)) < 0)
 528                 return st;
 529         st = 0;
 530         count = DiskInfo.last + 3;
 531         for (limit = 300; (limit > 0) && (count > 0); limit--) {
 532                 if ((st = optGetQChannelInfo(&qInfo)) < 0)
 533                         break;
 534                 px = bcd2bin(qInfo.pointIndex);
 535                 if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
 536                         if (Toc[px].pointIndex == 0) {
 537                                 Toc[px] = qInfo;
 538                                 count--;
 539                         }
 540         }
 541         Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
 542 #ifdef DEBUG_TOC
 543         printk("optcd: exiting optGetToc\n");
 544         for (i = 1; i <= DiskInfo.last + 1; i++)
 545                 printk("i = %3d ctl-adr = %02x track %2d px "
 546                         "%02x %02x:%02x.%02x %02x:%02x.%02x\n",
 547                         i, Toc[i].ctrl_addr,
 548                         Toc[i].track,
 549                         Toc[i].pointIndex,
 550                         Toc[i].trackTime.min,
 551                         Toc[i].trackTime.sec,
 552                         Toc[i].trackTime.frame,
 553                         Toc[i].diskTime.min,
 554                         Toc[i].diskTime.sec,
 555                         Toc[i].diskTime.frame);
 556         for (i = 100; i < 103; i++)
 557                 printk("i = %3d ctl-adr = %02x track %2d px "
 558                         "%02x %02x:%02x.%02x %02x:%02x.%02x\n",
 559                         i, Toc[i].ctrl_addr,
 560                         Toc[i].track,
 561                         Toc[i].pointIndex,
 562                         Toc[i].trackTime.min,
 563                         Toc[i].trackTime.sec,
 564                         Toc[i].trackTime.frame,
 565                         Toc[i].diskTime.min,
 566                         Toc[i].diskTime.sec,
 567                         Toc[i].diskTime.frame);
 568 #endif
 569         return count ? -ERR_TOC_MISSINGENTRY : 0;
 570 }
 571 
 572 static int optUpdateToc(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 573 #ifdef DEBUG_TOC
 574         printk("optcd: starting optUpdateToc\n");
 575 #endif
 576         if (optTocUpToDate)
 577                 return 0;
 578         if (optGetDiskInfo() < 0)
 579                 return -EIO;
 580         if (optGetToc() < 0)
 581                 return -EIO;
 582         optTocUpToDate = 1;
 583 #ifdef DEBUG_TOC
 584         printk("optcd: exiting optUpdateToc\n");
 585 #endif
 586         return 0;
 587 }
 588 
 589 
 590 /* Buffers */
 591 
 592 #define OPT_BUF_SIZ             16
 593 #define OPT_BLOCKSIZE           2048
 594 #define OPT_BLOCKSIZE_RAW       2336
 595 #define OPT_BLOCKSIZE_ALL       2646
 596 #define OPT_NOBUF               -1
 597 
 598 /* Buffer for block size conversion. */
 599 static char opt_buf[OPT_BLOCKSIZE*OPT_BUF_SIZ];
 600 static volatile int opt_buf_bn[OPT_BUF_SIZ], opt_next_bn;
 601 static volatile int opt_buf_in = 0, opt_buf_out = OPT_NOBUF;
 602 
 603 inline static void opt_invalidate_buffers(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 604         int i;
 605 #ifdef DEBUG_BUFFERS
 606         printk("optcd: executing opt_invalidate_buffers\n");
 607 #endif
 608         for (i = 0; i < OPT_BUF_SIZ; i++)
 609                 opt_buf_bn[i] = OPT_NOBUF;
 610         opt_buf_out = OPT_NOBUF;
 611 }
 612 
 613 /*
 614  * Take care of the different block sizes between cdrom and Linux.
 615  * When Linux gets variable block sizes this will probably go away.
 616  */
 617 static void opt_transfer(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 618 #if (defined DEBUG_BUFFERS) || (defined DEBUG_REQUEST)
 619         printk("optcd: executing opt_transfer\n");
 620 #endif
 621         if (!CURRENT_VALID)
 622                 return;
 623         while (CURRENT -> nr_sectors) {
 624                 int bn = CURRENT -> sector / 4;
 625                 int i, offs, nr_sectors;
 626                 for (i = 0; i < OPT_BUF_SIZ && opt_buf_bn[i] != bn; ++i);
 627 #ifdef DEBUG_REQUEST
 628                 printk("optcd: found %d\n", i);
 629 #endif
 630                 if (i >= OPT_BUF_SIZ) {
 631                         opt_buf_out = OPT_NOBUF;
 632                         break;
 633                 }
 634                 offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
 635                 nr_sectors = 4 - (CURRENT -> sector & 3);
 636                 if (opt_buf_out != i) {
 637                         opt_buf_out = i;
 638                         if (opt_buf_bn[i] != bn) {
 639                                 opt_buf_out = OPT_NOBUF;
 640                                 continue;
 641                         }
 642                 }
 643                 if (nr_sectors > CURRENT -> nr_sectors)
 644                         nr_sectors = CURRENT -> nr_sectors;
 645                 memcpy(CURRENT -> buffer, opt_buf + offs, nr_sectors * 512);
 646                 CURRENT -> nr_sectors -= nr_sectors;
 647                 CURRENT -> sector += nr_sectors;
 648                 CURRENT -> buffer += nr_sectors * 512;
 649         }
 650 }
 651 
 652 
 653 /* State machine for reading disk blocks */
 654 
 655 enum opt_state_e {
 656         OPT_S_IDLE,     /* 0 */
 657         OPT_S_START,    /* 1 */
 658         OPT_S_READ,     /* 2 */
 659         OPT_S_DATA,     /* 3 */
 660         OPT_S_STOP,     /* 4 */
 661         OPT_S_STOPPING  /* 5 */
 662 };
 663 
 664 static volatile enum opt_state_e opt_state = OPT_S_IDLE;
 665 #ifdef DEBUG_STATE
 666 static volatile enum opt_state_e opt_state_old = OPT_S_STOP;
 667 static volatile int opt_st_old = 0;
 668 static volatile long opt_state_n = 0;
 669 #endif
 670 
 671 static volatile int opt_transfer_is_active = 0;
 672 static volatile int opt_error = 0;      /* do something with this?? */
 673 static int optTries;                    /* ibid?? */
 674 
 675 static void opt_poll(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 676         static int optTimeout;
 677         static volatile int opt_read_count = 1;
 678         int st = 0;
 679         int loop_ctl = 1;
 680         int skip = 0;
 681 
 682         if (opt_error) {
 683                 printk("optcd: I/O error 0x%02x\n", opt_error);
 684                 opt_invalidate_buffers();
 685 #ifdef WARN_IF_READ_FAILURE
 686                 if (optTries == 5)
 687                         printk("optcd: read block %d failed; audio disk?\n",
 688                                 opt_next_bn);
 689 #endif
 690                 if (!optTries--) {
 691                         printk("optcd: read block %d failed; Giving up\n",
 692                                opt_next_bn);
 693                         if (opt_transfer_is_active) {
 694                                 optTries = 0;
 695                                 loop_ctl = 0;
 696                         }
 697                         if (CURRENT_VALID)
 698                                 end_request(0);
 699                         optTries = 5;
 700                 }
 701                 opt_error = 0;
 702                 opt_state = OPT_S_STOP;
 703         }
 704 
 705         while (loop_ctl)
 706         {
 707                 loop_ctl = 0; /* each case must flip this back to 1 if we want
 708                                  to come back up here */
 709 #ifdef DEBUG_STATE
 710                 if (opt_state == opt_state_old)
 711                         opt_state_n++;
 712                 else {
 713                         opt_state_old = opt_state;
 714                         if (++opt_state_n > 1)
 715                                 printk("optcd: %ld times in previous state\n",
 716                                         opt_state_n);
 717                         printk("optcd: state %d\n", opt_state);
 718                         opt_state_n = 0;
 719                 }
 720 #endif
 721                 switch (opt_state) {
 722                 case OPT_S_IDLE:
 723                         return;
 724                 case OPT_S_START:
 725                         if (optSendCmd(COMDRVST))
 726                                 return;
 727                         opt_state = OPT_S_READ;
 728                         optTimeout = 3000;
 729                         break;
 730                 case OPT_S_READ: {
 731                         struct opt_Play_msf msf;
 732                         if (!skip) {
 733                                 if ((st = optStatus()) < 0)
 734                                         break;
 735                                 if (st & ST_DSK_CHG) {
 736                                         optDiskChanged = 1;
 737                                         optTocUpToDate = 0;
 738                                         opt_invalidate_buffers();
 739                                 }
 740                         }
 741                         skip = 0;
 742                         if ((st & ST_DOOR_OPEN) || (st & ST_DRVERR)) {
 743                                 optDiskChanged = 1;
 744                                 optTocUpToDate = 0;
 745                                 printk((st & ST_DOOR_OPEN)
 746                                        ? "optcd: door open\n"
 747                                        : "optcd: disk removed\n");
 748                                 if (opt_transfer_is_active) {
 749                                         opt_state = OPT_S_START;
 750                                         loop_ctl = 1;
 751                                         break;
 752                                 }
 753                                 opt_state = OPT_S_IDLE;
 754                                 while (CURRENT_VALID)
 755                                         end_request(0);
 756                                 return;
 757                         }
 758                         if (!CURRENT_VALID) {
 759                                 opt_state = OPT_S_STOP;
 760                                 loop_ctl = 1;
 761                                 break;
 762                         }
 763                         opt_next_bn = CURRENT -> sector / 4;
 764                         hsg2msf(opt_next_bn, &msf.start);
 765                         opt_read_count = OPT_BUF_SIZ;
 766                         msf.end.min = 0;
 767                         msf.end.sec = 0;
 768                         msf.end.frame = opt_read_count;
 769 #ifdef DEBUG_REQUEST
 770                         printk("optcd: reading %x:%x.%x %x:%x.%x\n",
 771                                 msf.start.min,
 772                                 msf.start.sec,
 773                                 msf.start.frame,
 774                                 msf.end.min,
 775                                 msf.end.sec,
 776                                 msf.end.frame);
 777                         printk("optcd: opt_next_bn:%d opt_buf_in:%d opt_buf_out:%d opt_buf_bn:%d\n",
 778                                 opt_next_bn,
 779                                 opt_buf_in,
 780                                 opt_buf_out,
 781                                 opt_buf_bn[opt_buf_in]);
 782 #endif
 783                         optReadCmd(COMREAD, &msf);
 784                         opt_state = OPT_S_DATA;
 785                         optTimeout = READ_TIMEOUT;
 786                         break;
 787                 }
 788                 case OPT_S_DATA:
 789                         st = optFlags() & (FL_STEN|FL_DTEN);
 790 #ifdef DEBUG_STATE
 791                         if (st != opt_st_old) {
 792                                 opt_st_old = st;
 793                                 printk("optcd: st:%x\n", st);
 794                         }
 795                         if (st == FL_STEN)
 796                                 printk("timeout cnt: %d\n", optTimeout);
 797 #endif
 798                         switch (st) {
 799                         case FL_DTEN:
 800 #ifdef WARN_IF_READ_FAILURE
 801                                 if (optTries == 5)
 802                                         printk("optcd: read block %d failed; audio disk?\n",
 803                                                opt_next_bn);
 804 #endif
 805                                 if (!optTries--) {
 806                                         printk("optcd: read block %d failed; Giving up\n",
 807                                                opt_next_bn);
 808                                         if (opt_transfer_is_active) {
 809                                                 optTries = 0;
 810                                                 break;
 811                                         }
 812                                         if (CURRENT_VALID)
 813                                                 end_request(0);
 814                                         optTries = 5;
 815                                 }
 816                                 opt_state = OPT_S_START;
 817                                 optTimeout = READ_TIMEOUT;
 818                                 loop_ctl = 1;
 819                         case (FL_STEN|FL_DTEN):
 820                                 break;
 821                         default:
 822                                 optTries = 5;
 823                                 if (!CURRENT_VALID && opt_buf_in == opt_buf_out) {
 824                                         opt_state = OPT_S_STOP;
 825                                         loop_ctl = 1;
 826                                         break;
 827                                 }
 828                                 if (opt_read_count<=0)
 829                                         printk("optcd: warning - try to read 0 frames\n");
 830                                 while (opt_read_count) {
 831                                         opt_buf_bn[opt_buf_in] = OPT_NOBUF;
 832                                         if (dten_low()) { /* should be no waiting here!?? */
 833                                                 printk("read_count:%d CURRENT->nr_sectors:%ld opt_buf_in:%d\n",
 834                                                         opt_read_count,
 835                                                         CURRENT->nr_sectors,
 836                                                         opt_buf_in);
 837                                                 printk("opt_transfer_is_active:%x\n",
 838                                                         opt_transfer_is_active);
 839                                                 opt_read_count = 0;
 840                                                 opt_state = OPT_S_STOP;
 841                                                 loop_ctl = 1;
 842                                                 end_request(0);
 843                                                 break;
 844                                         }
 845                                         optReadData(opt_buf+OPT_BLOCKSIZE*opt_buf_in, OPT_BLOCKSIZE);
 846                                         opt_read_count--;
 847 #ifdef DEBUG_REQUEST
 848                                         printk("OPT_S_DATA; ---I've read data- read_count: %d\n",
 849                                                opt_read_count);
 850                                         printk("opt_next_bn:%d  opt_buf_in:%d opt_buf_out:%d  opt_buf_bn:%d\n",
 851                                                opt_next_bn,
 852                                                opt_buf_in,
 853                                                opt_buf_out,
 854                                                opt_buf_bn[opt_buf_in]);
 855 #endif
 856                                         opt_buf_bn[opt_buf_in] = opt_next_bn++;
 857                                         if (opt_buf_out == OPT_NOBUF)
 858                                                 opt_buf_out = opt_buf_in;
 859                                         opt_buf_in = opt_buf_in + 1 ==
 860                                                 OPT_BUF_SIZ ? 0 : opt_buf_in + 1;
 861                                 }
 862                                 if (!opt_transfer_is_active) {
 863                                         while (CURRENT_VALID) {
 864                                                 opt_transfer();
 865                                                 if (CURRENT -> nr_sectors == 0)
 866                                                         end_request(1);
 867                                                 else
 868                                                         break;
 869                                         }
 870                                 }
 871 
 872                                 if (CURRENT_VALID
 873                                     && (CURRENT -> sector / 4 < opt_next_bn ||
 874                                     CURRENT -> sector / 4 >
 875                                      opt_next_bn + OPT_BUF_SIZ)) {
 876                                         opt_state = OPT_S_STOP;
 877                                         loop_ctl = 1;
 878                                         break;
 879                                 }
 880                                 optTimeout = READ_TIMEOUT;
 881                                 if (opt_read_count == 0) {
 882                                         opt_state = OPT_S_STOP;
 883                                         loop_ctl = 1;
 884                                         break;
 885                                 }
 886                         }
 887                         break;
 888                 case OPT_S_STOP:
 889                         if (opt_read_count != 0)
 890                                 printk("optcd: discard data=%x frames\n",
 891                                         opt_read_count);
 892                         while (opt_read_count != 0) {
 893                                 optFlushData();
 894                                 opt_read_count--;
 895                         }
 896                         if (optSendCmd(COMDRVST))
 897                                 return;
 898                         opt_state = OPT_S_STOPPING;
 899                         optTimeout = 1000;
 900                         break;
 901                 case OPT_S_STOPPING:
 902                         if ((st = optStatus()) < 0 && optTimeout)
 903                                         break;
 904                         if ((st != -1) && (st & ST_DSK_CHG)) {
 905                                 optDiskChanged = 1;
 906                                 optTocUpToDate = 0;
 907                                 opt_invalidate_buffers();
 908                         }
 909                         if (CURRENT_VALID) {
 910                                 if (st != -1) {
 911                                         opt_state = OPT_S_READ;
 912                                         loop_ctl = 1;
 913                                         skip = 1;
 914                                         break;
 915                                 } else {
 916                                         opt_state = OPT_S_START;
 917                                         optTimeout = 1;
 918                                 }
 919                         } else {
 920                                 opt_state = OPT_S_IDLE;
 921                                 return;
 922                         }
 923                         break;
 924                 default:
 925                         printk("optcd: invalid state %d\n", opt_state);
 926                         return;
 927                 } /* case */
 928         } /* while */
 929 
 930         if (!optTimeout--) {
 931                 printk("optcd: timeout in state %d\n", opt_state);
 932                 opt_state = OPT_S_STOP;
 933                 if (optCmd(COMSTOP) < 0)
 934                         return;
 935         }
 936 
 937         SET_TIMER(opt_poll, 1);
 938 }
 939 
 940 
 941 static void do_optcd_request(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
 942 #ifdef DEBUG_REQUEST
 943         printk("optcd: do_optcd_request(%ld+%ld)\n",
 944                CURRENT -> sector, CURRENT -> nr_sectors);
 945 #endif
 946         opt_transfer_is_active = 1;
 947         while (CURRENT_VALID) {
 948                 if (CURRENT->bh) {
 949                         if (!CURRENT->bh->b_lock)
 950                                 panic(DEVICE_NAME ": block not locked");
 951                 }
 952                 opt_transfer(); /* First try to transfer block from buffers */
 953                 if (CURRENT -> nr_sectors == 0) {
 954                         end_request(1);
 955                 } else {        /* Want to read a block not in buffer */
 956                         opt_buf_out = OPT_NOBUF;
 957                         if (opt_state == OPT_S_IDLE) {
 958                                 /* Should this block the request queue?? */
 959                                 if (optUpdateToc() < 0) {
 960                                         while (CURRENT_VALID)
 961                                                 end_request(0);
 962                                         break;
 963                                 }
 964                                 /* Start state machine */
 965                                 opt_state = OPT_S_START;
 966                                 optTries = 5;
 967                                 SET_TIMER(opt_poll, 1); /* why not start right away?? */
 968                         }
 969                         break;
 970                 }
 971         }
 972         opt_transfer_is_active = 0;
 973 #ifdef DEBUG_REQUEST
 974         printk("opt_next_bn:%d  opt_buf_in:%d opt_buf_out:%d  opt_buf_bn:%d\n",
 975                opt_next_bn, opt_buf_in, opt_buf_out, opt_buf_bn[opt_buf_in]);
 976         printk("optcd: do_optcd_request ends\n");
 977 #endif
 978 }
 979 
 980 
 981 /* VFS calls */
 982 
 983 static int opt_ioctl(struct inode *ip, struct file *fp,
     /* [previous][next][first][last][top][bottom][index][help] */
 984                         unsigned int cmd, unsigned long arg) {
 985         static struct opt_Play_msf opt_Play;    /* pause position */
 986         int err;
 987 #ifdef DEBUG_VFS
 988         printk("optcd: starting opt_ioctl, command 0x%x\n", cmd);
 989 #endif
 990         if (!ip)
 991                 return -EINVAL;
 992         if (optGetStatus() < 0)
 993                 return -EIO;
 994         if ((err = optUpdateToc()) < 0)
 995                 return err;
 996 
 997         switch (cmd) {
 998         case CDROMPAUSE: {
 999                 struct opt_Toc qInfo;
1000 
1001                 if (optAudioStatus != CDROM_AUDIO_PLAY)
1002                         return -EINVAL;
1003                 if (optGetQChannelInfo(&qInfo) < 0) {
1004                         /* didn't get q channel info */
1005                         optAudioStatus = CDROM_AUDIO_NO_STATUS;
1006                         return 0;
1007                 }
1008                 opt_Play.start = qInfo.diskTime;        /* restart point */
1009                 if (optCmd(COMPAUSEON) < 0)
1010                         return -EIO;
1011                 optAudioStatus = CDROM_AUDIO_PAUSED;
1012                 break;
1013         }
1014         case CDROMRESUME:
1015                 if (optAudioStatus != CDROM_AUDIO_PAUSED)
1016                         return -EINVAL;
1017                 if (optPlayCmd(COMPLAY, &opt_Play) < 0) {
1018                         optAudioStatus = CDROM_AUDIO_ERROR;
1019                         return -EIO;
1020                 }
1021                 optAudioStatus = CDROM_AUDIO_PLAY;
1022                 break;
1023         case CDROMPLAYMSF: {
1024                 int st;
1025                 struct cdrom_msf msf;
1026 
1027                 if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof msf)))
1028                         return st;
1029                 memcpy_fromfs(&msf, (void *) arg, sizeof msf);
1030                 opt_Play.start.min = bin2bcd(msf.cdmsf_min0);
1031                 opt_Play.start.sec = bin2bcd(msf.cdmsf_sec0);
1032                 opt_Play.start.frame = bin2bcd(msf.cdmsf_frame0);
1033                 opt_Play.end.min = bin2bcd(msf.cdmsf_min1);
1034                 opt_Play.end.sec = bin2bcd(msf.cdmsf_sec1);
1035                 opt_Play.end.frame = bin2bcd(msf.cdmsf_frame1);
1036                 if (optPlayCmd(COMPLAY, &opt_Play) < 0) {
1037                         optAudioStatus = CDROM_AUDIO_ERROR;
1038                         return -EIO;
1039                 }
1040                 optAudioStatus = CDROM_AUDIO_PLAY;
1041                 break;
1042         }
1043         case CDROMPLAYTRKIND: {
1044                 int st;
1045                 struct cdrom_ti ti;
1046 
1047                 if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof ti)))
1048                         return st;
1049                 memcpy_fromfs(&ti, (void *) arg, sizeof ti);
1050                 if (ti.cdti_trk0 < DiskInfo.first
1051                         || ti.cdti_trk0 > DiskInfo.last
1052                         || ti.cdti_trk1 < ti.cdti_trk0)
1053                         return -EINVAL;
1054                 if (ti.cdti_trk1 > DiskInfo.last)
1055                         ti.cdti_trk1 = DiskInfo.last;
1056                 opt_Play.start = Toc[ti.cdti_trk0].diskTime;
1057                 opt_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
1058 #ifdef DEBUG_VFS
1059                 printk("optcd: play %02x:%02x.%02x to %02x:%02x.%02x\n",
1060                         opt_Play.start.min,
1061                         opt_Play.start.sec,
1062                         opt_Play.start.frame,
1063                         opt_Play.end.min,
1064                         opt_Play.end.sec,
1065                         opt_Play.end.frame);
1066 #endif
1067                 if (optPlayCmd(COMPLAY, &opt_Play) < 0) {
1068                         optAudioStatus = CDROM_AUDIO_ERROR;
1069                         return -EIO;
1070                 }
1071                 optAudioStatus = CDROM_AUDIO_PLAY;
1072                 break;
1073         }
1074         case CDROMREADTOCHDR: {         /* Read the table of contents header. */
1075                 int st;
1076                 struct cdrom_tochdr tocHdr;
1077 
1078                 if ((st = verify_area(VERIFY_WRITE,(void *)arg,sizeof tocHdr)))
1079                         return st;
1080                 if (!optTocUpToDate)
1081                         optGetDiskInfo();
1082                 tocHdr.cdth_trk0 = DiskInfo.first;
1083                 tocHdr.cdth_trk1 = DiskInfo.last;
1084                 memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
1085                 break;
1086         }
1087         case CDROMREADTOCENTRY: {       /* Read a table of contents entry. */
1088                 int st;
1089                 struct cdrom_tocentry entry;
1090                 struct opt_Toc *tocPtr;
1091 
1092                 if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof entry)))
1093                         return st;
1094                 if ((st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry)))
1095                         return st;
1096                 memcpy_fromfs(&entry, (void *) arg, sizeof entry);
1097                 if (!optTocUpToDate)
1098                         optGetDiskInfo();
1099                 if (entry.cdte_track == CDROM_LEADOUT)
1100                         tocPtr = &Toc[DiskInfo.last + 1];
1101                 else if (entry.cdte_track > DiskInfo.last
1102                         || entry.cdte_track < DiskInfo.first)
1103                         return -EINVAL;
1104                 else
1105                         tocPtr = &Toc[entry.cdte_track];
1106                 entry.cdte_adr = tocPtr -> ctrl_addr;
1107                 entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
1108                 switch (entry.cdte_format) {
1109                 case CDROM_LBA:
1110                         entry.cdte_addr.lba = msf2hsg(&tocPtr -> diskTime);
1111                         break;
1112                 case CDROM_MSF:
1113                         entry.cdte_addr.msf.minute =
1114                                 bcd2bin(tocPtr -> diskTime.min);
1115                         entry.cdte_addr.msf.second =
1116                                 bcd2bin(tocPtr -> diskTime.sec);
1117                         entry.cdte_addr.msf.frame =
1118                                 bcd2bin(tocPtr -> diskTime.frame);
1119                         break;
1120                 default:
1121                         return -EINVAL;
1122                 }
1123                 memcpy_tofs((void *) arg, &entry, sizeof entry);
1124                 break;
1125         }
1126         case CDROMSTOP:
1127                 optCmd(COMSTOP);
1128                 optAudioStatus = CDROM_AUDIO_NO_STATUS;
1129                 break;
1130         case CDROMSTART:
1131                 optCmd(COMCLOSE);       /* What else can we do? */
1132                 break;
1133         case CDROMEJECT:
1134                 optCmd(COMUNLOCK);
1135                 optCmd(COMOPEN);
1136                 break;
1137         case CDROMVOLCTRL: {
1138                 int st;
1139                 struct cdrom_volctrl volctrl;
1140 
1141                 if ((st = verify_area(VERIFY_READ, (void *) arg,
1142                                 sizeof(volctrl))))
1143                         return st;
1144                 memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
1145                 opt_Play.start.min = 0x10;
1146                 opt_Play.start.sec = 0x32;
1147                 opt_Play.start.frame = volctrl.channel0;
1148                 opt_Play.end.min = volctrl.channel1;
1149                 opt_Play.end.sec = volctrl.channel2;
1150                 opt_Play.end.frame = volctrl.channel3;
1151                 if (optPlayCmd(COMCHCTRL, &opt_Play) < 0)
1152                         return -EIO;
1153                 break;
1154         }
1155         case CDROMSUBCHNL: {    /* Get subchannel info */
1156                 int st;
1157                 struct cdrom_subchnl subchnl;
1158                 struct opt_Toc qInfo;
1159 
1160                 if ((st = verify_area(VERIFY_READ,
1161                                 (void *) arg, sizeof subchnl)))
1162                         return st;
1163                 if ((st = verify_area(VERIFY_WRITE,
1164                                 (void *) arg, sizeof subchnl)))
1165                         return st;
1166                 memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl);
1167                 if (optGetQChannelInfo(&qInfo) < 0)
1168                         return -EIO;
1169                 subchnl.cdsc_audiostatus = optAudioStatus;
1170                 subchnl.cdsc_adr = qInfo.ctrl_addr;
1171                 subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4;
1172                 subchnl.cdsc_trk = bcd2bin(qInfo.track);
1173                 subchnl.cdsc_ind = bcd2bin(qInfo.pointIndex);
1174                 switch (subchnl.cdsc_format) {
1175                 case CDROM_LBA:
1176                         subchnl.cdsc_absaddr.lba = msf2hsg(&qInfo.diskTime);
1177                         subchnl.cdsc_reladdr.lba = msf2hsg(&qInfo.trackTime);
1178                         break;
1179                 case CDROM_MSF:
1180                         subchnl.cdsc_absaddr.msf.minute =
1181                                 bcd2bin(qInfo.diskTime.min);
1182                         subchnl.cdsc_absaddr.msf.second =
1183                                 bcd2bin(qInfo.diskTime.sec);
1184                         subchnl.cdsc_absaddr.msf.frame =
1185                                 bcd2bin(qInfo.diskTime.frame);
1186                         subchnl.cdsc_reladdr.msf.minute =
1187                                 bcd2bin(qInfo.trackTime.min);
1188                         subchnl.cdsc_reladdr.msf.second =
1189                                 bcd2bin(qInfo.trackTime.sec);
1190                         subchnl.cdsc_reladdr.msf.frame =
1191                                 bcd2bin(qInfo.trackTime.frame);
1192                         break;
1193                 default:
1194                         return -EINVAL;
1195                 }
1196                 memcpy_tofs((void *) arg, &subchnl, sizeof subchnl);
1197                 break;
1198         }
1199         case CDROMREADMODE1: {
1200                 int st;
1201                 struct cdrom_msf msf;
1202                 char buf[OPT_BLOCKSIZE];
1203 
1204                 if ((st = verify_area(VERIFY_READ, (void *) arg, sizeof msf)))
1205                         return st;
1206                 if ((st = verify_area(VERIFY_WRITE,(void *)arg,OPT_BLOCKSIZE)))
1207                         return st;
1208                 memcpy_fromfs(&msf, (void *) arg, sizeof msf);
1209                 opt_Play.start.min = bin2bcd(msf.cdmsf_min0);
1210                 opt_Play.start.sec = bin2bcd(msf.cdmsf_sec0);
1211                 opt_Play.start.frame = bin2bcd(msf.cdmsf_frame0);
1212                 opt_Play.end.min = 0;
1213                 opt_Play.end.sec = 0;
1214                 opt_Play.end.frame = 1; /* read only one frame */
1215                 st = optReadCmd(COMREAD, &opt_Play);
1216 #ifdef DEBUG_VFS
1217                 printk("optcd: COMREAD status 0x%x\n", st);
1218 #endif
1219                 sleep_dten_low();       /* error checking here?? */
1220                 optReadData(buf, OPT_BLOCKSIZE);
1221                 memcpy_tofs((void *) arg, &buf, OPT_BLOCKSIZE);
1222                 break;
1223         }
1224         case CDROMMULTISESSION:
1225                 return -EINVAL; /* unluckily, not implemented yet */
1226 
1227         default:
1228                 return -EINVAL;
1229         }
1230 #ifdef DEBUG_VFS
1231         printk("optcd: exiting opt_ioctl\n");
1232 #endif
1233         return 0;
1234 }
1235 
1236 static int optPresent = 0;
1237 static int opt_open_count = 0;
1238 
1239 /* Open device special file; check that a disk is in. */
1240 static int opt_open(struct inode *ip, struct file *fp) {
     /* [previous][next][first][last][top][bottom][index][help] */
1241 #ifdef DEBUG_VFS
1242         printk("optcd: starting opt_open\n");
1243 #endif
1244         if (!optPresent)
1245                 return -ENXIO;          /* no hardware */
1246         if (!opt_open_count && opt_state == OPT_S_IDLE) {
1247                 int st;
1248                 opt_invalidate_buffers();
1249                 if ((st = optGetStatus()) < 0)
1250                         return -EIO;
1251                 if (st & ST_DOOR_OPEN) {
1252                         optCmd(COMCLOSE);                       /* close door */
1253                         if ((st = optGetStatus()) < 0)          /* try again */
1254                                 return -EIO;
1255                 }
1256                 if (st & (ST_DOOR_OPEN|ST_DRVERR)) {
1257                         printk("optcd: no disk or door open\n");
1258                         return -EIO;
1259                 }
1260                 if (optUpdateToc() < 0)
1261                         return -EIO;
1262         }
1263         opt_open_count++;
1264         MOD_INC_USE_COUNT;
1265         optCmd(COMLOCK);                /* Lock door */
1266 #ifdef DEBUG_VFS
1267         printk("optcd: exiting opt_open\n");
1268 #endif
1269         return 0;
1270 }
1271 
1272 /* Release device special file; flush all blocks from the buffer cache */
1273 static void opt_release(struct inode *ip, struct file *fp) {
     /* [previous][next][first][last][top][bottom][index][help] */
1274 #ifdef DEBUG_VFS
1275         printk("optcd: executing opt_release\n");
1276         printk("inode: %p, inode -> i_rdev: 0x%x, file: %p\n",
1277                 ip, ip -> i_rdev, fp);
1278 #endif
1279         if (!--opt_open_count) {
1280                 opt_invalidate_buffers();
1281                 sync_dev(ip -> i_rdev);
1282                 invalidate_buffers(ip -> i_rdev);
1283                 CLEAR_TIMER;
1284                 optCmd(COMUNLOCK);      /* Unlock door */
1285         }
1286         MOD_DEC_USE_COUNT;
1287 }
1288 
1289 
1290 /* Initialisation */
1291 
1292 static int version_ok(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
1293         char devname[100];
1294         int count, i, ch;
1295 
1296         if (optCmd(COMVERSION) < 0)
1297                 return 0;
1298         if ((count = optGetData()) < 0)
1299                 return 0;
1300         for (i = 0, ch = -1; count > 0; count--) {
1301                 if ((ch = optGetData()) < 0)
1302                         break;
1303                 if (i < 99)
1304                         devname[i++] = ch;
1305         }
1306         devname[i] = '\0';
1307         if (ch < 0)
1308                 return 0;
1309         printk("optcd: Device %s detected\n", devname);
1310         return ((devname[0] == 'D')
1311              && (devname[1] == 'O')
1312              && (devname[2] == 'L')
1313              && (devname[3] == 'P')
1314              && (devname[4] == 'H')
1315              && (devname[5] == 'I')
1316              && (devname[6] == 'N'));
1317 }
1318 
1319 
1320 static struct file_operations opt_fops = {
1321         NULL,           /* lseek - default */
1322         block_read,     /* read - general block-dev read */
1323         block_write,    /* write - general block-dev write */
1324         NULL,           /* readdir - bad */
1325         NULL,           /* select */
1326         opt_ioctl,      /* ioctl */
1327         NULL,           /* mmap */
1328         opt_open,       /* open */
1329         opt_release,    /* release */
1330         NULL,           /* fsync */
1331         NULL,           /* fasync */
1332         NULL,           /* media change */
1333         NULL            /* revalidate */
1334 };
1335 
1336 
1337 /* Get kernel parameter when used as a kernel driver */
1338 void optcd_setup(char *str, int *ints) {
     /* [previous][next][first][last][top][bottom][index][help] */
1339         if (ints[0] > 0)
1340                 optcd_port = ints[1];
1341 }
1342 
1343 #ifndef MODULE
1344 #define RETURN_EIO return mem_start
1345 #else
1346 #define RETURN_EIO return -EIO
1347 #endif
1348 
1349 /*
1350  * Test for presence of drive and initialize it. Called at boot time.
1351  */
1352 #ifndef MODULE
1353 unsigned long optcd_init(unsigned long mem_start, unsigned long mem_end) {
     /* [previous][next][first][last][top][bottom][index][help] */
1354 #else
1355 int init_module(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
1356 #endif
1357         if (optcd_port <= 0) {
1358                 printk("optcd: no Optics Storage CDROM Initialization\n");
1359                 RETURN_EIO;
1360         }
1361         if (check_region(optcd_port, 4)) {
1362                 printk("optcd: conflict, I/O port 0x%x already used\n",
1363                         optcd_port);
1364                 RETURN_EIO;
1365         }
1366         if (!optResetDrive()) {
1367                 printk("optcd: drive at 0x%x not ready\n", optcd_port);
1368                 RETURN_EIO;
1369         }
1370         if (!version_ok()) {
1371                 printk("optcd: unknown drive detected; aborting\n");
1372                 RETURN_EIO;
1373         }
1374         if (optCmd(COMINITDOUBLE) < 0) {
1375                 printk("optcd: cannot init double speed mode\n");
1376                 RETURN_EIO;
1377         }
1378         if (register_blkdev(MAJOR_NR, "optcd", &opt_fops) != 0)
1379         {
1380                 printk("optcd: unable to get major %d\n", MAJOR_NR);
1381                 RETURN_EIO;
1382         }
1383         blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
1384         read_ahead[MAJOR_NR] = 4;
1385         request_region(optcd_port, 4, "optcd");
1386         optPresent = 1;
1387         printk("optcd: 8000 AT CDROM at 0x%x\n", optcd_port);
1388 #ifndef MODULE
1389         return mem_start;
1390 #else
1391         return 0;
1392 #endif
1393 }
1394 
1395 #ifdef MODULE
1396 void cleanup_module(void) {
     /* [previous][next][first][last][top][bottom][index][help] */
1397         if (MOD_IN_USE) {
1398                 printk("optcd: module in use - can't remove it.\n");
1399         return;
1400         }
1401         if ((unregister_blkdev(MAJOR_NR, "optcd") == -EINVAL)) {
1402                 printk("optcd: what's that: can't unregister\n");
1403                 return;
1404         }
1405         release_region(optcd_port, 4);
1406         printk("optcd: module released.\n");
1407 }
1408 #endif MODULE

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