summaryrefslogtreecommitdiffstats
path: root/drivers/cdrom/optcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/cdrom/optcd.c')
-rw-r--r--drivers/cdrom/optcd.c2105
1 files changed, 0 insertions, 2105 deletions
diff --git a/drivers/cdrom/optcd.c b/drivers/cdrom/optcd.c
deleted file mode 100644
index efd619c0fe1..00000000000
--- a/drivers/cdrom/optcd.c
+++ /dev/null
@@ -1,2105 +0,0 @@
-/* linux/drivers/cdrom/optcd.c - Optics Storage 8000 AT CDROM driver
- $Id: optcd.c,v 1.11 1997/01/26 07:13:00 davem Exp $
-
- Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl)
-
-
- Based on Aztech CD268 CDROM driver by Werner Zimmermann and preworks
- by Eberhard Moenkeberg (emoenke@gwdg.de).
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-/* Revision history
-
-
- 14-5-95 v0.0 Plays sound tracks. No reading of data CDs yet.
- Detection of disk change doesn't work.
- 21-5-95 v0.1 First ALPHA version. CD can be mounted. The
- device major nr is borrowed from the Aztech
- driver. Speed is around 240 kb/s, as measured
- with "time dd if=/dev/cdrom of=/dev/null \
- bs=2048 count=4096".
- 24-6-95 v0.2 Reworked the #defines for the command codes
- and the like, as well as the structure of
- the hardware communication protocol, to
- reflect the "official" documentation, kindly
- supplied by C.K. Tan, Optics Storage Pte. Ltd.
- Also tidied up the state machine somewhat.
- 28-6-95 v0.3 Removed the ISP-16 interface code, as this
- should go into its own driver. The driver now
- has its own major nr.
- Disk change detection now seems to work, too.
- This version became part of the standard
- kernel as of version 1.3.7
- 24-9-95 v0.4 Re-inserted ISP-16 interface code which I
- copied from sjcd.c, with a few changes.
- Updated README.optcd. Submitted for
- inclusion in 1.3.21
- 29-9-95 v0.4a Fixed bug that prevented compilation as module
- 25-10-95 v0.5 Started multisession code. Implementation
- copied from Werner Zimmermann, who copied it
- from Heiko Schlittermann's mcdx.
- 17-1-96 v0.6 Multisession works; some cleanup too.
- 18-4-96 v0.7 Increased some timing constants;
- thanks to Luke McFarlane. Also tidied up some
- printk behaviour. ISP16 initialization
- is now handled by a separate driver.
-
- 09-11-99 Make kernel-parameter implementation work with 2.3.x
- Removed init_module & cleanup_module in favor of
- module_init & module_exit.
- Torben Mathiasen <tmm@image.dk>
-*/
-
-/* Includes */
-
-
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-
-#include <asm/io.h>
-#include <linux/blkdev.h>
-
-#include <linux/cdrom.h>
-#include "optcd.h"
-
-#include <asm/uaccess.h>
-
-#define MAJOR_NR OPTICS_CDROM_MAJOR
-#define QUEUE (opt_queue)
-#define CURRENT elv_next_request(opt_queue)
-
-
-/* Debug support */
-
-
-/* Don't forget to add new debug flags here. */
-#if DEBUG_DRIVE_IF | DEBUG_VFS | DEBUG_CONV | DEBUG_TOC | \
- DEBUG_BUFFERS | DEBUG_REQUEST | DEBUG_STATE | DEBUG_MULTIS
-#define DEBUG(x) debug x
-static void debug(int debug_this, const char* fmt, ...)
-{
- char s[1024];
- va_list args;
-
- if (!debug_this)
- return;
-
- va_start(args, fmt);
- vsnprintf(s, sizeof(s), fmt, args);
- printk(KERN_DEBUG "optcd: %s\n", s);
- va_end(args);
-}
-#else
-#define DEBUG(x)
-#endif
-
-
-/* Drive hardware/firmware characteristics
- Identifiers in accordance with Optics Storage documentation */
-
-
-#define optcd_port optcd /* Needed for the modutils. */
-static short optcd_port = OPTCD_PORTBASE; /* I/O base of drive. */
-module_param(optcd_port, short, 0);
-/* Drive registers, read */
-#define DATA_PORT optcd_port /* Read data/status */
-#define STATUS_PORT optcd_port+1 /* Indicate data/status availability */
-
-/* Drive registers, write */
-#define COMIN_PORT optcd_port /* For passing command/parameter */
-#define RESET_PORT optcd_port+1 /* Write anything and wait 0.5 sec */
-#define HCON_PORT optcd_port+2 /* Host Xfer Configuration */
-
-
-/* Command completion/status read from DATA register */
-#define ST_DRVERR 0x80
-#define ST_DOOR_OPEN 0x40
-#define ST_MIXEDMODE_DISK 0x20
-#define ST_MODE_BITS 0x1c
-#define ST_M_STOP 0x00
-#define ST_M_READ 0x04
-#define ST_M_AUDIO 0x04
-#define ST_M_PAUSE 0x08
-#define ST_M_INITIAL 0x0c
-#define ST_M_ERROR 0x10
-#define ST_M_OTHERS 0x14
-#define ST_MODE2TRACK 0x02
-#define ST_DSK_CHG 0x01
-#define ST_L_LOCK 0x01
-#define ST_CMD_OK 0x00
-#define ST_OP_OK 0x01
-#define ST_PA_OK 0x02
-#define ST_OP_ERROR 0x05
-#define ST_PA_ERROR 0x06
-
-
-/* Error codes (appear as command completion code from DATA register) */
-/* Player related errors */
-#define ERR_ILLCMD 0x11 /* Illegal command to player module */
-#define ERR_ILLPARM 0x12 /* Illegal parameter to player module */
-#define ERR_SLEDGE 0x13
-#define ERR_FOCUS 0x14
-#define ERR_MOTOR 0x15
-#define ERR_RADIAL 0x16
-#define ERR_PLL 0x17 /* PLL lock error */
-#define ERR_SUB_TIM 0x18 /* Subcode timeout error */
-#define ERR_SUB_NF 0x19 /* Subcode not found error */
-#define ERR_TRAY 0x1a
-#define ERR_TOC 0x1b /* Table of Contents read error */
-#define ERR_JUMP 0x1c
-/* Data errors */
-#define ERR_MODE 0x21
-#define ERR_FORM 0x22
-#define ERR_HEADADDR 0x23 /* Header Address not found */
-#define ERR_CRC 0x24
-#define ERR_ECC 0x25 /* Uncorrectable ECC error */
-#define ERR_CRC_UNC 0x26 /* CRC error and uncorrectable error */
-#define ERR_ILLBSYNC 0x27 /* Illegal block sync error */
-#define ERR_VDST 0x28 /* VDST not found */
-/* Timeout errors */
-#define ERR_READ_TIM 0x31 /* Read timeout error */
-#define ERR_DEC_STP 0x32 /* Decoder stopped */
-#define ERR_DEC_TIM 0x33 /* Decoder interrupt timeout error */
-/* Function abort codes */
-#define ERR_KEY 0x41 /* Key -Detected abort */
-#define ERR_READ_FINISH 0x42 /* Read Finish */
-/* Second Byte diagnostic codes */
-#define ERR_NOBSYNC 0x01 /* No block sync */
-#define ERR_SHORTB 0x02 /* Short block */
-#define ERR_LONGB 0x03 /* Long block */
-#define ERR_SHORTDSP 0x04 /* Short DSP word */
-#define ERR_LONGDSP 0x05 /* Long DSP word */
-
-
-/* Status availability flags read from STATUS register */
-#define FL_EJECT 0x20
-#define FL_WAIT 0x10 /* active low */
-#define FL_EOP 0x08 /* active low */
-#define FL_STEN 0x04 /* Status available when low */
-#define FL_DTEN 0x02 /* Data available when low */
-#define FL_DRQ 0x01 /* active low */
-#define FL_RESET 0xde /* These bits are high after a reset */
-#define FL_STDT (FL_STEN|FL_DTEN)
-
-
-/* Transfer mode, written to HCON register */
-#define HCON_DTS 0x08
-#define HCON_SDRQB 0x04
-#define HCON_LOHI 0x02
-#define HCON_DMA16 0x01
-
-
-/* Drive command set, written to COMIN register */
-/* Quick response commands */
-#define COMDRVST 0x20 /* Drive Status Read */
-#define COMERRST 0x21 /* Error Status Read */
-#define COMIOCTLISTAT 0x22 /* Status Read; reset disk changed bit */
-#define COMINITSINGLE 0x28 /* Initialize Single Speed */
-#define COMINITDOUBLE 0x29 /* Initialize Double Speed */
-#define COMUNLOCK 0x30 /* Unlock */
-#define COMLOCK 0x31 /* Lock */
-#define COMLOCKST 0x32 /* Lock/Unlock Status */
-#define COMVERSION 0x40 /* Get Firmware Revision */
-#define COMVOIDREADMODE 0x50 /* Void Data Read Mode */
-/* Read commands */
-#define COMFETCH 0x60 /* Prefetch Data */
-#define COMREAD 0x61 /* Read */
-#define COMREADRAW 0x62 /* Read Raw Data */
-#define COMREADALL 0x63 /* Read All 2646 Bytes */
-/* Player control commands */
-#define COMLEADIN 0x70 /* Seek To Lead-in */
-#define COMSEEK 0x71 /* Seek */
-#define COMPAUSEON 0x80 /* Pause On */
-#define COMPAUSEOFF 0x81 /* Pause Off */
-#define COMSTOP 0x82 /* Stop */
-#define COMOPEN 0x90 /* Open Tray Door */
-#define COMCLOSE 0x91 /* Close Tray Door */
-#define COMPLAY 0xa0 /* Audio Play */
-#define COMPLAY_TNO 0xa2 /* Audio Play By Track Number */
-#define COMSUBQ 0xb0 /* Read Sub-q Code */
-#define COMLOCATION 0xb1 /* Read Head Position */
-/* Audio control commands */
-#define COMCHCTRL 0xc0 /* Audio Channel Control */
-/* Miscellaneous (test) commands */
-#define COMDRVTEST 0xd0 /* Write Test Bytes */
-#define COMTEST 0xd1 /* Diagnostic Test */
-
-/* Low level drive interface. Only here we do actual I/O
- Waiting for status / data available */
-
-
-/* Busy wait until FLAG goes low. Return 0 on timeout. */
-static inline int flag_low(int flag, unsigned long timeout)
-{
- int flag_high;
- unsigned long count = 0;
-
- while ((flag_high = (inb(STATUS_PORT) & flag)))
- if (++count >= timeout)
- break;
-
- DEBUG((DEBUG_DRIVE_IF, "flag_low 0x%x count %ld%s",
- flag, count, flag_high ? " timeout" : ""));
- return !flag_high;
-}
-
-
-/* Timed waiting for status or data */
-static int sleep_timeout; /* max # of ticks to sleep */
-static DECLARE_WAIT_QUEUE_HEAD(waitq);
-static void sleep_timer(unsigned long data);
-static DEFINE_TIMER(delay_timer, sleep_timer, 0, 0);
-static DEFINE_SPINLOCK(optcd_lock);
-static struct request_queue *opt_queue;
-
-/* Timer routine: wake up when desired flag goes low,
- or when timeout expires. */
-static void sleep_timer(unsigned long data)
-{
- int flags = inb(STATUS_PORT) & FL_STDT;
-
- if (flags == FL_STDT && --sleep_timeout > 0) {
- mod_timer(&delay_timer, jiffies + HZ/100); /* multi-statement macro */
- } else
- wake_up(&waitq);
-}
-
-
-/* Sleep until FLAG goes low. Return 0 on timeout or wrong flag low. */
-static int sleep_flag_low(int flag, unsigned long timeout)
-{
- int flag_high;
-
- DEBUG((DEBUG_DRIVE_IF, "sleep_flag_low"));
-
- sleep_timeout = timeout;
- flag_high = inb(STATUS_PORT) & flag;
- if (flag_high && sleep_timeout > 0) {
- mod_timer(&delay_timer, jiffies + HZ/100);
- sleep_on(&waitq);
- flag_high = inb(STATUS_PORT) & flag;
- }
-
- DEBUG((DEBUG_DRIVE_IF, "flag 0x%x count %ld%s",
- flag, timeout, flag_high ? " timeout" : ""));
- return !flag_high;
-}
-
-/* Low level drive interface. Only here we do actual I/O
- Sending commands and parameters */
-
-
-/* Errors in the command protocol */
-#define ERR_IF_CMD_TIMEOUT 0x100
-#define ERR_IF_ERR_TIMEOUT 0x101
-#define ERR_IF_RESP_TIMEOUT 0x102
-#define ERR_IF_DATA_TIMEOUT 0x103
-#define ERR_IF_NOSTAT 0x104
-
-
-/* Send command code. Return <0 indicates error */
-static int send_cmd(int cmd)
-{
- unsigned char ack;
-
- DEBUG((DEBUG_DRIVE_IF, "sending command 0x%02x\n", cmd));
-
- outb(HCON_DTS, HCON_PORT); /* Enable Suspend Data Transfer */
- outb(cmd, COMIN_PORT); /* Send command code */
- if (!flag_low(FL_STEN, BUSY_TIMEOUT)) /* Wait for status */
- return -ERR_IF_CMD_TIMEOUT;
- ack = inb(DATA_PORT); /* read command acknowledge */
- outb(HCON_SDRQB, HCON_PORT); /* Disable Suspend Data Transfer */
- return ack==ST_OP_OK ? 0 : -ack;
-}
-
-
-/* Send command parameters. Return <0 indicates error */
-static int send_params(struct cdrom_msf *params)
-{
- unsigned char ack;
-
- DEBUG((DEBUG_DRIVE_IF, "sending parameters"
- " %02x:%02x:%02x"
- " %02x:%02x:%02x",
- params->cdmsf_min0,
- params->cdmsf_sec0,
- params->cdmsf_frame0,
- params->cdmsf_min1,
- params->cdmsf_sec1,
- params->cdmsf_frame1));
-
- outb(params->cdmsf_min0, COMIN_PORT);
- outb(params->cdmsf_sec0, COMIN_PORT);
- outb(params->cdmsf_frame0, COMIN_PORT);
- outb(params->cdmsf_min1, COMIN_PORT);
- outb(params->cdmsf_sec1, COMIN_PORT);
- outb(params->cdmsf_frame1, COMIN_PORT);
- if (!flag_low(FL_STEN, BUSY_TIMEOUT)) /* Wait for status */
- return -ERR_IF_CMD_TIMEOUT;
- ack = inb(DATA_PORT); /* read command acknowledge */
- return ack==ST_PA_OK ? 0 : -ack;
-}
-
-
-/* Send parameters for SEEK command. Return <0 indicates error */
-static int send_seek_params(struct cdrom_msf *params)
-{
- unsigned char ack;
-
- DEBUG((DEBUG_DRIVE_IF, "sending seek parameters"
- " %02x:%02x:%02x",
- params->cdmsf_min0,
- params->cdmsf_sec0,
- params->cdmsf_frame0));
-
- outb(params->cdmsf_min0, COMIN_PORT);
- outb(params->cdmsf_sec0, COMIN_PORT);
- outb(params->cdmsf_frame0, COMIN_PORT);
- if (!flag_low(FL_STEN, BUSY_TIMEOUT)) /* Wait for status */
- return -ERR_IF_CMD_TIMEOUT;
- ack = inb(DATA_PORT); /* read command acknowledge */
- return ack==ST_PA_OK ? 0 : -ack;
-}
-
-
-/* Wait for command execution status. Choice between busy waiting
- and sleeping. Return value <0 indicates timeout. */
-static inline int get_exec_status(int busy_waiting)
-{
- unsigned char exec_status;
-
- if (busy_waiting
- ? !flag_low(FL_STEN, BUSY_TIMEOUT)
- : !sleep_flag_low(FL_STEN, SLEEP_TIMEOUT))
- return -ERR_IF_CMD_TIMEOUT;
-
- exec_status = inb(DATA_PORT);
- DEBUG((DEBUG_DRIVE_IF, "returned exec status 0x%02x", exec_status));
- return exec_status;
-}
-
-
-/* Wait busy for extra byte of data that a command returns.
- Return value <0 indicates timeout. */
-static inline int get_data(int short_timeout)
-{
- unsigned char data;
-
- if (!flag_low(FL_STEN, short_timeout ? FAST_TIMEOUT : BUSY_TIMEOUT))
- return -ERR_IF_DATA_TIMEOUT;
-
- data = inb(DATA_PORT);
- DEBUG((DEBUG_DRIVE_IF, "returned data 0x%02x", data));
- return data;
-}
-
-
-/* Returns 0 if failed */
-static int reset_drive(void)
-{
- unsigned long count = 0;
- int flags;
-
- DEBUG((DEBUG_DRIVE_IF, "reset drive"));
-
- outb(0, RESET_PORT);
- while (++count < RESET_WAIT)
- inb(DATA_PORT);
-
- count = 0;
- while ((flags = (inb(STATUS_PORT) & FL_RESET)) != FL_RESET)
- if (++count >= BUSY_TIMEOUT)
- break;
-
- DEBUG((DEBUG_DRIVE_IF, "reset %s",
- flags == FL_RESET ? "succeeded" : "failed"));
-
- if (flags != FL_RESET)
- return 0; /* Reset failed */
- outb(HCON_SDRQB, HCON_PORT); /* Disable Suspend Data Transfer */
- return 1; /* Reset succeeded */
-}
-
-
-/* Facilities for asynchronous operation */
-
-/* Read status/data availability flags FL_STEN and FL_DTEN */
-static inline int stdt_flags(void)
-{
- return inb(STATUS_PORT) & FL_STDT;
-}
-
-
-/* Fetch status that has previously been waited for. <0 means not available */
-static inline int fetch_status(void)
-{
- unsigned char status;
-
- if (inb(STATUS_PORT) & FL_STEN)
- return -ERR_IF_NOSTAT;
-
- status = inb(DATA_PORT);
- DEBUG((DEBUG_DRIVE_IF, "fetched exec status 0x%02x", status));
- return status;
-}
-
-
-/* Fetch data that has previously been waited for. */
-static inline void fetch_data(char *buf, int n)
-{
- insb(DATA_PORT, buf, n);
- DEBUG((DEBUG_DRIVE_IF, "fetched 0x%x bytes", n));
-}
-
-
-/* Flush status and data fifos */
-static inline void flush_data(void)
-{
- while ((inb(STATUS_PORT) & FL_STDT) != FL_STDT)
- inb(DATA_PORT);
- DEBUG((DEBUG_DRIVE_IF, "flushed fifos"));
-}
-
-/* Command protocol */
-
-
-/* Send a simple command and wait for response. Command codes < COMFETCH
- are quick response commands */
-static inline int exec_cmd(int cmd)
-{
- int ack = send_cmd(cmd);
- if (ack < 0)
- return ack;
- return get_exec_status(cmd < COMFETCH);
-}
-
-
-/* Send a command with parameters. Don't wait for the response,
- * which consists of data blocks read from the CD. */
-static inline int exec_read_cmd(int cmd, struct cdrom_msf *params)
-{
- int ack = send_cmd(cmd);
- if (ack < 0)
- return ack;
- return send_params(params);
-}
-
-
-/* Send a seek command with parameters and wait for response */
-static inline int exec_seek_cmd(int cmd, struct cdrom_msf *params)
-{
- int ack = send_cmd(cmd);
- if (ack < 0)
- return ack;
- ack = send_seek_params(params);
- if (ack < 0)
- return ack;
- return 0;
-}
-
-
-/* Send a command with parameters and wait for response */
-static inline int exec_long_cmd(int cmd, struct cdrom_msf *params)
-{
- int ack = exec_read_cmd(cmd, params);
- if (ack < 0)
- return ack;
- return get_exec_status(0);
-}
-
-/* Address conversion routines */
-
-
-/* Binary to BCD (2 digits) */
-static inline void single_bin2bcd(u_char *p)
-{
- DEBUG((DEBUG_CONV, "bin2bcd %02d", *p));
- *p = (*p % 10) | ((*p / 10) << 4);
-}
-
-
-/* Convert entire msf struct */
-static void bin2bcd(struct cdrom_msf *msf)
-{
- single_bin2bcd(&msf->cdmsf_min0);
- single_bin2bcd(&msf->cdmsf_sec0);
- single_bin2bcd(&msf->cdmsf_frame0);
- single_bin2bcd(&msf->cdmsf_min1);
- single_bin2bcd(&msf->cdmsf_sec1);
- single_bin2bcd(&msf->cdmsf_frame1);
-}
-
-
-/* Linear block address to minute, second, frame form */
-#define CD_FPM (CD_SECS * CD_FRAMES) /* frames per minute */
-
-static void lba2msf(int lba, struct cdrom_msf *msf)
-{
- DEBUG((DEBUG_CONV, "lba2msf %d", lba));
- lba += CD_MSF_OFFSET;
- msf->cdmsf_min0 = lba / CD_FPM; lba %= CD_FPM;
- msf->cdmsf_sec0 = lba / CD_FRAMES;
- msf->cdmsf_frame0 = lba % CD_FRAMES;
- msf->cdmsf_min1 = 0;
- msf->cdmsf_sec1 = 0;
- msf->cdmsf_frame1 = 0;
- bin2bcd(msf);
-}
-
-
-/* Two BCD digits to binary */
-static inline u_char bcd2bin(u_char bcd)
-{
- DEBUG((DEBUG_CONV, "bcd2bin %x%02x", bcd));
- return (bcd >> 4) * 10 + (bcd & 0x0f);
-}
-
-
-static void msf2lba(union cdrom_addr *addr)
-{
- addr->lba = addr->msf.minute * CD_FPM
- + addr->msf.second * CD_FRAMES
- + addr->msf.frame - CD_MSF_OFFSET;
-}
-
-
-/* Minute, second, frame address BCD to binary or to linear address,
- depending on MODE */
-static void msf_bcd2bin(union cdrom_addr *addr)
-{
- addr->msf.minute = bcd2bin(addr->msf.minute);
- addr->msf.second = bcd2bin(addr->msf.second);
- addr->msf.frame = bcd2bin(addr->msf.frame);
-}
-
-/* High level drive commands */
-
-
-static int audio_status = CDROM_AUDIO_NO_STATUS;
-static char toc_uptodate = 0;
-static char disk_changed = 1;
-
-/* Get drive status, flagging completion of audio play and disk changes. */
-static int drive_status(void)
-{
- int status;
-
- status = exec_cmd(COMIOCTLISTAT);
- DEBUG((DEBUG_DRIVE_IF, "IOCTLISTAT: %03x", status));
- if (status < 0)
- return status;
- if (status == 0xff) /* No status available */
- return -ERR_IF_NOSTAT;
-
- if (((status & ST_MODE_BITS) != ST_M_AUDIO) &&
- (audio_status == CDROM_AUDIO_PLAY)) {
- audio_status = CDROM_AUDIO_COMPLETED;
- }
-
- if (status & ST_DSK_CHG) {
- toc_uptodate = 0;
- disk_changed = 1;
- audio_status = CDROM_AUDIO_NO_STATUS;
- }
-
- return status;
-}
-
-
-/* Read the current Q-channel info. Also used for reading the
- table of contents. qp->cdsc_format must be set on entry to
- indicate the desired address format */
-static int get_q_channel(struct cdrom_subchnl *qp)
-{
- int status, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10;
-
- status = drive_status();
- if (status < 0)
- return status;
- qp->cdsc_audiostatus = audio_status;
-
- status = exec_cmd(COMSUBQ);
- if (status < 0)
- return status;
-
- d1 = get_data(0);
- if (d1 < 0)
- return d1;
- qp->cdsc_adr = d1;
- qp->cdsc_ctrl = d1 >> 4;
-
- d2 = get_data(0);
- if (d2 < 0)
- return d2;
- qp->cdsc_trk = bcd2bin(d2);
-
- d3 = get_data(0);
- if (d3 < 0)
- return d3;
- qp->cdsc_ind = bcd2bin(d3);
-
- d4 = get_data(0);
- if (d4 < 0)
- return d4;
- qp->cdsc_reladdr.msf.minute = d4;
-
- d5 = get_data(0);
- if (d5 < 0)
- return d5;
- qp->cdsc_reladdr.msf.second = d5;
-
- d6 = get_data(0);
- if (d6 < 0)
- return d6;
- qp->cdsc_reladdr.msf.frame = d6;
-
- d7 = get_data(0);
- if (d7 < 0)
- return d7;
- /* byte not used */
-
- d8 = get_data(0);
- if (d8 < 0)
- return d8;
- qp->cdsc_absaddr.msf.minute = d8;
-
- d9 = get_data(0);
- if (d9 < 0)
- return d9;
- qp->cdsc_absaddr.msf.second = d9;
-
- d10 = get_data(0);
- if (d10 < 0)
- return d10;
- qp->cdsc_absaddr.msf.frame = d10;
-
- DEBUG((DEBUG_TOC, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
- d1, d2, d3, d4, d5, d6, d7, d8, d9, d10));
-
- msf_bcd2bin(&qp->cdsc_absaddr);
- msf_bcd2bin(&qp->cdsc_reladdr);
- if (qp->cdsc_format == CDROM_LBA) {
- msf2lba(&qp->cdsc_absaddr);
- msf2lba(&qp->cdsc_reladdr);
- }
-
- return 0;
-}
-
-/* Table of contents handling */
-
-
-/* Errors in table of contents */
-#define ERR_TOC_MISSINGINFO 0x120
-#define ERR_TOC_MISSINGENTRY 0x121
-
-
-struct cdrom_disk_info {
- unsigned char first;
- unsigned char last;
- struct cdrom_msf0 disk_length;
- struct cdrom_msf0 first_track;
- /* Multisession info: */
- unsigned char next;
- struct cdrom_msf0 next_session;
- struct cdrom_msf0 last_session;
- unsigned char multi;
- unsigned char xa;
- unsigned char audio;
-};
-static struct cdrom_disk_info disk_info;
-
-#define MAX_TRACKS 111
-static struct cdrom_subchnl toc[MAX_TRACKS];
-
-#define QINFO_FIRSTTRACK 100 /* bcd2bin(0xa0) */
-#define QINFO_LASTTRACK 101 /* bcd2bin(0xa1) */
-#define QINFO_DISKLENGTH 102 /* bcd2bin(0xa2) */
-#define QINFO_NEXTSESSION 110 /* bcd2bin(0xb0) */
-
-#define I_FIRSTTRACK 0x01
-#define I_LASTTRACK 0x02
-#define I_DISKLENGTH 0x04
-#define I_NEXTSESSION 0x08
-#define I_ALL (I_FIRSTTRACK | I_LASTTRACK | I_DISKLENGTH)
-
-
-#if DEBUG_TOC
-static void toc_debug_info(int i)
-{
- printk(KERN_DEBUG "#%3d ctl %1x, adr %1x, track %2d index %3d"
- " %2d:%02d.%02d %2d:%02d.%02d\n",
- i, toc[i].cdsc_ctrl, toc[i].cdsc_adr,
- toc[i].cdsc_trk, toc[i].cdsc_ind,
- toc[i].cdsc_reladdr.msf.minute,
- toc[i].cdsc_reladdr.msf.second,
- toc[i].cdsc_reladdr.msf.frame,
- toc[i].cdsc_absaddr.msf.minute,
- toc[i].cdsc_absaddr.msf.second,
- toc[i].cdsc_absaddr.msf.frame);
-}
-#endif
-
-
-static int read_toc(void)
-{
- int status, limit, count;
- unsigned char got_info = 0;
- struct cdrom_subchnl q_info;
-#if DEBUG_TOC
- int i;
-#endif
-
- DEBUG((DEBUG_TOC, "starting read_toc"));
-
- count = 0;
- for (limit = 60; limit > 0; limit--) {
- int index;
-
- q_info.cdsc_format = CDROM_MSF;
- status = get_q_channel(&q_info);
- if (status < 0)
- return status;
-
- index = q_info.cdsc_ind;
- if (index > 0 && index < MAX_TRACKS
- && q_info.cdsc_trk == 0 && toc[index].cdsc_ind == 0) {
- toc[index] = q_info;
- DEBUG((DEBUG_TOC, "got %d", index));
- if (index < 100)
- count++;
-
- switch (q_info.cdsc_ind) {
- case QINFO_FIRSTTRACK:
- got_info |= I_FIRSTTRACK;
- break;
- case QINFO_LASTTRACK:
- got_info |= I_LASTTRACK;
- break;
- case QINFO_DISKLENGTH:
- got_info |= I_DISKLENGTH;
- break;
- case QINFO_NEXTSESSION:
- got_info |= I_NEXTSESSION;
- break;
- }
- }
-
- if ((got_info & I_ALL) == I_ALL
- && toc[QINFO_FIRSTTRACK].cdsc_absaddr.msf.minute + count
- >= toc[QINFO_LASTTRACK].cdsc_absaddr.msf.minute + 1)
- break;
- }
-
- /* Construct disk_info from TOC */
- if (disk_info.first == 0) {
- disk_info.first = toc[QINFO_FIRSTTRACK].cdsc_absaddr.msf.minute;
- disk_info.first_track.minute =
- toc[disk_info.first].cdsc_absaddr.msf.minute;
- disk_info.first_track.second =
- toc[disk_info.first].cdsc_absaddr.msf.second;
- disk_info.first_track.frame =
- toc[disk_info.first].cdsc_absaddr.msf.frame;
- }
- disk_info.last = toc[QINFO_LASTTRACK].cdsc_absaddr.msf.minute;
- disk_info.disk_length.minute =
- toc[QINFO_DISKLENGTH].cdsc_absaddr.msf.minute;
- disk_info.disk_length.second =
- toc[QINFO_DISKLENGTH].cdsc_absaddr.msf.second-2;
- disk_info.disk_length.frame =
- toc[QINFO_DISKLENGTH].cdsc_absaddr.msf.frame;
- disk_info.next_session.minute =
- toc[QINFO_NEXTSESSION].cdsc_reladdr.msf.minute;
- disk_info.next_session.second =
- toc[QINFO_NEXTSESSION].cdsc_reladdr.msf.second;
- disk_info.next_session.frame =
- toc[QINFO_NEXTSESSION].cdsc_reladdr.msf.frame;
- disk_info.next = toc[QINFO_FIRSTTRACK].cdsc_absaddr.msf.minute;
- disk_info.last_session.minute =
- toc[disk_info.next].cdsc_absaddr.msf.minute;
- disk_info.last_session.second =
- toc[disk_info.next].cdsc_absaddr.msf.second;
- disk_info.last_session.frame =
- toc[disk_info.next].cdsc_absaddr.msf.frame;
- toc[disk_info.last + 1].cdsc_absaddr.msf.minute =
- disk_info.disk_length.minute;
- toc[disk_info.last + 1].cdsc_absaddr.msf.second =
- disk_info.disk_length.second;
- toc[disk_info.last + 1].cdsc_absaddr.msf.frame =
- disk_info.disk_length.frame;
-#if DEBUG_TOC
- for (i = 1; i <= disk_info.last + 1; i++)
- toc_debug_info(i);
- toc_debug_info(QINFO_FIRSTTRACK);
- toc_debug_info(QINFO_LASTTRACK);
- toc_debug_info(QINFO_DISKLENGTH);
- toc_debug_info(QINFO_NEXTSESSION);
-#endif
-
- DEBUG((DEBUG_TOC, "exiting read_toc, got_info %x, count %d",
- got_info, count));
- if ((got_info & I_ALL) != I_ALL
- || toc[QINFO_FIRSTTRACK].cdsc_absaddr.msf.minute + count
- < toc[QINFO_LASTTRACK].cdsc_absaddr.msf.minute + 1)
- return -ERR_TOC_MISSINGINFO;
- return 0;
-}
-
-
-#ifdef MULTISESSION
-static int get_multi_disk_info(void)
-{
- int sessions, status;
- struct cdrom_msf multi_index;
-
-
- for (sessions = 2; sessions < 10 /* %%for now */; sessions++) {
- int count;
-
- for (count = 100; count < MAX_TRACKS; count++)
- toc[count].cdsc_ind = 0;
-
- multi_index.cdmsf_min0 = disk_info.next_session.minute;
- multi_index.cdmsf_sec0 = disk_info.next_session.second;
- multi_index.cdmsf_frame0 = disk_info.next_session.frame;
- if (multi_index.cdmsf_sec0 >= 20)
- multi_index.cdmsf_sec0 -= 20;
- else {
- multi_index.cdmsf_sec0 += 40;
- multi_index.cdmsf_min0--;
- }
- DEBUG((DEBUG_MULTIS, "Try %d: %2d:%02d.%02d", sessions,
- multi_index.cdmsf_min0,
- multi_index.cdmsf_sec0,
- multi_index.cdmsf_frame0));
- bin2bcd(&multi_index);
- multi_index.cdmsf_min1 = 0;
- multi_index.cdmsf_sec1 = 0;
- multi_index.cdmsf_frame1 = 1;
-
- status = exec_read_cmd(COMREAD, &multi_index);
- if (status < 0) {
- DEBUG((DEBUG_TOC, "exec_read_cmd COMREAD: %02x",
- -status));
- break;
- }
- status = sleep_flag_low(FL_DTEN, MULTI_SEEK_TIMEOUT) ?
- 0 : -ERR_TOC_MISSINGINFO;
- flush_data();
- if (status < 0) {
- DEBUG((DEBUG_TOC, "sleep_flag_low: %02x", -status));
- break;
- }
-
- status = read_toc();
- if (status < 0) {
- DEBUG((DEBUG_TOC, "read_toc: %02x", -status));
- break;
- }
-
- disk_info.multi = 1;
- }
-
- exec_cmd(COMSTOP);
-
- if (status < 0)
- return -EIO;
- return 0;
-}
-#endif /* MULTISESSION */
-
-
-static int update_toc(void)
-{
- int status, count;
-
- if (toc_uptodate)
- return 0;
-
- DEBUG((DEBUG_TOC, "starting update_toc"));
-
- disk_info.first = 0;
- for (count = 0; count < MAX_TRACKS; count++)
- toc[count].cdsc_ind = 0;
-
- status = exec_cmd(COMLEADIN);
- if (status < 0)
- return -EIO;
-
- status = read_toc();
- if (status < 0) {
- DEBUG((DEBUG_TOC, "read_toc: %02x", -status));
- return -EIO;
- }
-
- /* Audio disk detection. Look at first track. */
- disk_info.audio =
- (toc[disk_info.first].cdsc_ctrl & CDROM_DATA_TRACK) ? 0 : 1;
-
- /* XA detection */
- disk_info.xa = drive_status() & ST_MODE2TRACK;
-
- /* Multisession detection: if we want this, define MULTISESSION */
- disk_info.multi = 0;
-#ifdef MULTISESSION
- if (disk_info.xa)
- get_multi_disk_info(); /* Here disk_info.multi is set */
-#endif /* MULTISESSION */
- if (disk_info.multi)
- printk(KERN_WARNING "optcd: Multisession support experimental, "
- "see Documentation/cdrom/optcd\n");
-
- DEBUG((DEBUG_TOC, "exiting update_toc"));
-
- toc_uptodate = 1;
- return 0;
-}
-
-/* Request handling */
-
-static int current_valid(void)
-{
- return CURRENT &&
- rq_data_dir(CURRENT) == READ &&
- CURRENT->sector != -1;
-}
-
-/* Buffers for block size conversion. */
-#define NOBUF -1
-
-static char buf[CD_FRAMESIZE * N_BUFS];
-static volatile int buf_bn[N_BUFS], next_bn;
-static volatile int buf_in = 0, buf_out = NOBUF;
-
-static inline void opt_invalidate_buffers(void)
-{
- int i;
-
- DEBUG((DEBUG_BUFFERS, "executing opt_invalidate_buffers"));
-
- for (i = 0; i < N_BUFS; i++)
- buf_bn[i] = NOBUF;
- buf_out = NOBUF;
-}
-
-
-/* Take care of the different block sizes between cdrom and Linux.
- When Linux gets variable block sizes this will probably go away. */
-static void transfer(void)
-{
-#if DEBUG_BUFFERS | DEBUG_REQUEST
- printk(KERN_DEBUG "optcd: executing transfer\n");
-#endif
-
- if (!current_valid())
- return;
- while (CURRENT -> nr_sectors) {
- int bn = CURRENT -> sector / 4;
- int i, offs, nr_sectors;
- for (i = 0; i < N_BUFS && buf_bn[i] != bn; ++i);
-
- DEBUG((DEBUG_REQUEST, "found %d", i));
-
- if (i >= N_BUFS) {
- buf_out = NOBUF;
- break;
- }
-
- offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
- nr_sectors = 4 - (CURRENT -> sector & 3);
-
- if (buf_out != i) {
- buf_out = i;
- if (buf_bn[i] != bn) {
- buf_out = NOBUF;
- continue;
- }
- }
-
- if (nr_sectors > CURRENT -> nr_sectors)
- nr_sectors = CURRENT -> nr_sectors;
- memcpy(CURRENT -> buffer, buf + offs, nr_sectors * 512);
- CURRENT -> nr_sectors -= nr_sectors;
- CURRENT -> sector += nr_sectors;
- CURRENT -> buffer += nr_sectors * 512;
- }
-}
-
-
-/* State machine for reading disk blocks */
-
-enum state_e {
- S_IDLE, /* 0 */
- S_START, /* 1 */
- S_READ, /* 2 */
- S_DATA, /* 3 */
- S_STOP, /* 4 */
- S_STOPPING /* 5 */
-};
-
-static volatile enum state_e state = S_IDLE;
-#if DEBUG_STATE
-static volatile enum state_e state_old = S_STOP;
-static volatile int flags_old = 0;
-static volatile long state_n = 0;
-#endif
-
-
-/* Used as mutex to keep do_optcd_request (and other processes calling
- ioctl) out while some process is inside a VFS call.
- Reverse is accomplished by checking if state = S_IDLE upon entry
- of opt_ioctl and opt_media_change. */
-static int in_vfs = 0;
-
-
-static volatile int transfer_is_active = 0;
-static volatile int error = 0; /* %% do something with this?? */
-static int tries; /* ibid?? */
-static int timeout = 0;
-
-static void poll(unsigned long data);
-static struct timer_list req_timer = {.function = poll};
-
-
-static void poll(unsigned long data)
-{
- static volatile int read_count = 1;
- int flags;
- int loop_again = 1;
- int status = 0;
- int skip = 0;
-
- if (error) {
- printk(KERN_ERR "optcd: I/O error 0x%02x\n", error);
- opt_invalidate_buffers();
- if (!tries--) {
- printk(KERN_ERR "optcd: read block %d failed;"
- " Giving up\n", next_bn);
- if (transfer_is_active)
- loop_again = 0;
- if (current_valid())
- end_request(CURRENT, 0);
- tries = 5;
- }
- error = 0;
- state = S_STOP;
- }
-
- while (loop_again)
- {
- loop_again = 0; /* each case must flip this back to 1 if we want
- to come back up here */
-
-#if DEBUG_STATE
- if (state == state_old)
- state_n++;
- else {
- state_old = state;
- if (++state_n > 1)
- printk(KERN_DEBUG "optcd: %ld times "
- "in previous state\n", state_n);
- printk(KERN_DEBUG "optcd: state %d\n", state);
- state_n = 0;
- }
-#endif
-
- switch (state) {
- case S_IDLE:
- return;
- case S_START:
- if (in_vfs)
- break;
- if (send_cmd(COMDRVST)) {
- state = S_IDLE;
- while (current_valid())
- end_request(CURRENT, 0);
- return;
- }
- state = S_READ;
- timeout = READ_TIMEOUT;
- break;
- case S_READ: {
- struct cdrom_msf msf;
- if (!skip) {
- status = fetch_status();
- if (status < 0)
- break;
- if (status & ST_DSK_CHG) {
- toc_uptodate = 0;
- opt_invalidate_buffers();
- }
- }
- skip = 0;
- if ((status & ST_DOOR_OPEN) || (status & ST_DRVERR)) {
- toc_uptodate = 0;
- opt_invalidate_buffers();
- printk(KERN_WARNING "optcd: %s\n",
- (status & ST_DOOR_OPEN)
- ? "door open"
- : "disk removed");
- state = S_IDLE;
- while (current_valid())
- end_request(CURRENT, 0);
- return;
- }
- if (!current_valid()) {
- state = S_STOP;
- loop_again = 1;
- break;
- }
- next_bn = CURRENT -> sector / 4;
- lba2msf(next_bn, &msf);
- read_count = N_BUFS;
- msf.cdmsf_frame1 = read_count; /* Not BCD! */
-
- DEBUG((DEBUG_REQUEST, "reading %x:%x.%x %x:%x.%x",
- msf.cdmsf_min0,
- msf.cdmsf_sec0,
- msf.cdmsf_frame0,
- msf.cdmsf_min1,
- msf.cdmsf_sec1,
- msf.cdmsf_frame1));
- DEBUG((DEBUG_REQUEST, "next_bn:%d buf_in:%d"
- " buf_out:%d buf_bn:%d",
- next_bn,
- buf_in,
- buf_out,
- buf_bn[buf_in]));
-
- exec_read_cmd(COMREAD, &msf);
- state = S_DATA;
- timeout = READ_TIMEOUT;
- break;
- }
- case S_DATA:
- flags = stdt_flags() & (FL_STEN|FL_DTEN);
-
-#if DEBUG_STATE
- if (flags != flags_old) {
- flags_old = flags;
- printk(KERN_DEBUG "optcd: flags:%x\n", flags);
- }
- if (flags == FL_STEN)
- printk(KERN_DEBUG "timeout cnt: %d\n", timeout);
-#endif
-
- switch (flags) {
- case FL_DTEN: /* only STEN low */
- if (!tries--) {
- printk(KERN_ERR
- "optcd: read block %d failed; "
- "Giving up\n", next_bn);
- if (transfer_is_active) {
- tries = 0;
- break;
- }
- if (current_valid())
- end_request(CURRENT, 0);
- tries = 5;
- }
- state = S_START;
- timeout = READ_TIMEOUT;
- loop_again = 1;
- case (FL_STEN|FL_DTEN): /* both high */
- break;
- default: /* DTEN low */
- tries = 5;
- if (!current_valid() && buf_in == buf_out) {
- state = S_STOP;
- loop_again = 1;
- break;
- }
- if (read_count<=0)
- printk(KERN_WARNING
- "optcd: warning - try to read"
- " 0 frames\n");
- while (read_count) {
- buf_bn[buf_in] = NOBUF;
- if (!flag_low(FL_DTEN, BUSY_TIMEOUT)) {
- /* should be no waiting here!?? */
- printk(KERN_ERR
- "read_count:%d "
- "CURRENT->nr_sectors:%ld "
- "buf_in:%d\n",
- read_count,
- CURRENT->nr_sectors,
- buf_in);
- printk(KERN_ERR
- "transfer active: %x\n",
- transfer_is_active);
- read_count = 0;
- state = S_STOP;
- loop_again = 1;
- end_request(CURRENT, 0);
- break;
- }
- fetch_data(buf+
- CD_FRAMESIZE*buf_in,
- CD_FRAMESIZE);
- read_count--;
-
- DEBUG((DEBUG_REQUEST,
- "S_DATA; ---I've read data- "
- "read_count: %d",
- read_count));
- DEBUG((DEBUG_REQUEST,
- "next_bn:%d buf_in:%d "
- "buf_out:%d buf_bn:%d",
- next_bn,
- buf_in,
- buf_out,
- buf_bn[buf_in]));
-
- buf_bn[buf_in] = next_bn++;
- if (buf_out == NOBUF)
- buf_out = buf_in;
- buf_in = buf_in + 1 ==
- N_BUFS ? 0 : buf_in + 1;
- }
- if (!transfer_is_active) {
- while (current_valid()) {
- transfer();
- if (CURRENT -> nr_sectors == 0)
- end_request(CURRENT, 1);
- else
- break;
- }
- }
-
- if (current_valid()
- && (CURRENT -> sector / 4 < next_bn ||
- CURRENT -> sector / 4 >
- next_bn + N_BUFS)) {
- state = S_STOP;
- loop_again = 1;
- break;
- }
- timeout = READ_TIMEOUT;
- if (read_count == 0) {
- state = S_STOP;
- loop_again = 1;
- break;
- }
- }
- break;
- case S_STOP:
- if (read_count != 0)
- printk(KERN_ERR
- "optcd: discard data=%x frames\n",
- read_count);
- flush_data();
- if (send_cmd(COMDRVST)) {
- state = S_IDLE;
- while (current_valid())
- end_request(CURRENT, 0);
- return;
- }
- state = S_STOPPING;
- timeout = STOP_TIMEOUT;
- break;
- case S_STOPPING:
- status = fetch_status();
- if (status < 0 && timeout)
- break;
- if ((status >= 0) && (status & ST_DSK_CHG)) {
- toc_uptodate = 0;
- opt_invalidate_buffers();
- }
- if (current_valid()) {
- if (status >= 0) {
- state = S_READ;
- loop_again = 1;
- skip = 1;
- break;
- } else {
- state = S_START;
- timeout = 1;
- }
- } else {
- state = S_IDLE;
- return;
- }
- break;
- default:
- printk(KERN_ERR "optcd: invalid state %d\n", state);
- return;
- } /* case */
- } /* while */
-
- if (!timeout--) {
- printk(KERN_ERR "optcd: timeout in state %d\n", state);
- state = S_STOP;
- if (exec_cmd(COMSTOP) < 0) {
- state = S_IDLE;
- while (current_valid())
- end_request(CURRENT, 0);
- return;
- }
- }
-
- mod_timer(&req_timer, jiffies + HZ/100);
-}
-
-
-static void do_optcd_request(request_queue_t * q)
-{
- DEBUG((DEBUG_REQUEST, "do_optcd_request(%ld+%ld)",
- CURRENT -> sector, CURRENT -> nr_sectors));
-
- if (disk_info.audio) {
- printk(KERN_WARNING "optcd: tried to mount an Audio CD\n");
- end_request(CURRENT, 0);
- return;
- }
-
- transfer_is_active = 1;
- while (current_valid()) {
- transfer(); /* First try to transfer block from buffers */
- if (CURRENT -> nr_sectors == 0) {
- end_request(CURRENT, 1);
- } else { /* Want to read a block not in buffer */
- buf_out = NOBUF;
- if (state == S_IDLE) {
- /* %% Should this block the request queue?? */
- if (update_toc() < 0) {
- while (current_valid())
- end_request(CURRENT, 0);
- break;
- }
- /* Start state machine */
- state = S_START;
- timeout = READ_TIMEOUT;
- tries = 5;
- /* %% why not start right away?? */
- mod_timer(&req_timer, jiffies + HZ/100);
- }
- break;
- }
- }
- transfer_is_active = 0;
-
- DEBUG((DEBUG_REQUEST, "next_bn:%d buf_in:%d buf_out:%d buf_bn:%d",
- next_bn, buf_in, buf_out, buf_bn[buf_in]));
- DEBUG((DEBUG_REQUEST, "do_optcd_request ends"));
-}
-
-/* IOCTLs */
-
-
-static char auto_eject = 0;
-
-static int cdrompause(void)
-{
- int status;
-
- if (audio_status != CDROM_AUDIO_PLAY)
- return -EINVAL;
-
- status = exec_cmd(COMPAUSEON);
- if (status < 0) {
- DEBUG((DEBUG_VFS, "exec_cmd COMPAUSEON: %02x", -status));
- return -EIO;
- }
- audio_status = CDROM_AUDIO_PAUSED;
- return 0;
-}
-
-
-static int cdromresume(void)
-{
- int status;
-
- if (audio_status != CDROM_AUDIO_PAUSED)
- return -EINVAL;
-
- status = exec_cmd(COMPAUSEOFF);
- if (status < 0) {
- DEBUG((DEBUG_VFS, "exec_cmd COMPAUSEOFF: %02x", -status));
- audio_status = CDROM_AUDIO_ERROR;
- return -EIO;
- }
- audio_status = CDROM_AUDIO_PLAY;
- return 0;
-}
-
-
-static int cdromplaymsf(void __user *arg)
-{
- int status;
- struct cdrom_msf msf;
-
- if (copy_from_user(&msf, arg, sizeof msf))
- return -EFAULT;
-
- bin2bcd(&msf);
- status = exec_long_cmd(COMPLAY, &msf);
- if (status < 0) {
- DEBUG((DEBUG_VFS, "exec_long_cmd COMPLAY: %02x", -status));
- audio_status = CDROM_AUDIO_ERROR;
- return -EIO;
- }
-
- audio_status = CDROM_AUDIO_PLAY;
- return 0;
-}
-
-
-static int cdromplaytrkind(void __user *arg)
-{
- int status;
- struct cdrom_ti ti;
- struct cdrom_msf msf;
-
- if (copy_from_user(&ti, arg, sizeof ti))
- return -EFAULT;
-
- if (ti.cdti_trk0 < disk_info.first
- || ti.cdti_trk0 > disk_info.last
- || ti.cdti_trk1 < ti.cdti_trk0)
- return -EINVAL;
- if (ti.cdti_trk1 > disk_info.last)
- ti.cdti_trk1 = disk_info.last;
-
- msf.cdmsf_min0 = toc[ti.cdti_trk0].cdsc_absaddr.msf.minute;
- msf.cdmsf_sec0 = toc[ti.cdti_trk0].cdsc_absaddr.msf.second;
- msf.cdmsf_frame0 = toc[ti.cdti_trk0].cdsc_absaddr.msf.frame;
- msf.cdmsf_min1 = toc[ti.cdti_trk1 + 1].cdsc_absaddr.msf.minute;
- msf.cdmsf_sec1 = toc[ti.cdti_trk1 + 1].cdsc_absaddr.msf.second;
- msf.cdmsf_frame1 = toc[ti.cdti_trk1 + 1].cdsc_absaddr.msf.frame;
-
- DEBUG((DEBUG_VFS, "play %02d:%02d.%02d to %02d:%02d.%02d",
- msf.cdmsf_min0,
- msf.cdmsf_sec0,
- msf.cdmsf_frame0,
- msf.cdmsf_min1,
- msf.cdmsf_sec1,
- msf.cdmsf_frame1));
-
- bin2bcd(&msf);
- status = exec_long_cmd(COMPLAY, &msf);
- if (status < 0) {
- DEBUG((DEBUG_VFS, "exec_long_cmd COMPLAY: %02x", -status));
- audio_status = CDROM_AUDIO_ERROR;
- return -EIO;
- }
-
- audio_status = CDROM_AUDIO_PLAY;
- return 0;
-}
-
-
-static int cdromreadtochdr(void __user *arg)
-{
- struct cdrom_tochdr tochdr;
-
- tochdr.cdth_trk0 = disk_info.first;
- tochdr.cdth_trk1 = disk_info.last;
-
- return copy_to_user(arg, &tochdr, sizeof tochdr) ? -EFAULT : 0;
-}
-
-
-static int cdromreadtocentry(void __user *arg)
-{
- struct cdrom_tocentry entry;
- struct cdrom_subchnl *tocptr;
-
- if (copy_from_user(&entry, arg, sizeof entry))
- return -EFAULT;
-
- if (entry.cdte_track == CDROM_LEADOUT)
- tocptr = &toc[disk_info.last + 1];
- else if (entry.cdte_track > disk_info.last
- || entry.cdte_track < disk_info.first)
- return -EINVAL;
- else
- tocptr = &toc[entry.cdte_track];
-
- entry.cdte_adr = tocptr->cdsc_adr;
- entry.cdte_ctrl = tocptr->cdsc_ctrl;
- entry.cdte_addr.msf.minute = tocptr->cdsc_absaddr.msf.minute;
- entry.cdte_addr.msf.second = tocptr->cdsc_absaddr.msf.second;
- entry.cdte_addr.msf.frame = tocptr->cdsc_absaddr.msf.frame;
- /* %% What should go into entry.cdte_datamode? */
-
- if (entry.cdte_format == CDROM_LBA)
- msf2lba(&entry.cdte_addr);
- else if (entry.cdte_format != CDROM_MSF)
- return -EINVAL;
-
- return copy_to_user(arg, &entry, sizeof entry) ? -EFAULT : 0;
-}
-
-
-static int cdromvolctrl(void __user *arg)
-{
- int status;
- struct cdrom_volctrl volctrl;
- struct cdrom_msf msf;
-
- if (copy_from_user(&volctrl, arg, sizeof volctrl))
- return -EFAULT;
-
- msf.cdmsf_min0 = 0x10;
- msf.cdmsf_sec0 = 0x32;
- msf.cdmsf_frame0 = volctrl.channel0;
- msf.cdmsf_min1 = volctrl.channel1;
- msf.cdmsf_sec1 = volctrl.channel2;
- msf.cdmsf_frame1 = volctrl.channel3;
-
- status = exec_long_cmd(COMCHCTRL, &msf);
- if (status < 0) {
- DEBUG((DEBUG_VFS, "exec_long_cmd COMCHCTRL: %02x", -status));
- return -EIO;
- }
- return 0;
-}
-
-
-static int cdromsubchnl(void __user *arg)
-{
- int status;
- struct cdrom_subchnl subchnl;
-
- if (copy_from_user(&subchnl, arg, sizeof subchnl))
- return -EFAULT;
-
- if (subchnl.cdsc_format != CDROM_LBA
- && subchnl.cdsc_format != CDROM_MSF)
- return -EINVAL;
-
- status = get_q_channel(&subchnl);
- if (status < 0) {
- DEBUG((DEBUG_VFS, "get_q_channel: %02x", -status));
- return -EIO;
- }
-
- if (copy_to_user(arg, &subchnl, sizeof subchnl))
- return -EFAULT;
- return 0;
-}
-
-
-static struct gendisk *optcd_disk;
-
-
-static int cdromread(void __user *arg, int blocksize, int cmd)
-{
- int status;
- struct cdrom_msf msf;
-
- if (copy_from_user(&msf, arg, sizeof msf))
- return -EFAULT;
-
- bin2bcd(&msf);
- msf.cdmsf_min1 = 0;
- msf.cdmsf_sec1 = 0;
- msf.cdmsf_frame1 = 1; /* read only one frame */
- status = exec_read_cmd(cmd, &msf);
-
- DEBUG((DEBUG_VFS, "read cmd status 0x%x", status));
-
- if (!sleep_flag_low(FL_DTEN, SLEEP_TIMEOUT))
- return -EIO;
-
- fetch_data(optcd_disk->private_data, blocksize);
-
- if (copy_to_user(arg, optcd_disk->private_data, blocksize))
- return -EFAULT;
-
- return 0;
-}
-
-
-static int cdromseek(void __user *arg)
-{
- int status;
- struct cdrom_msf msf;
-
- if (copy_from_user(&msf, arg, sizeof msf))
- return -EFAULT;
-
- bin2bcd(&msf);
- status = exec_seek_cmd(COMSEEK, &msf);
-
- DEBUG((DEBUG_VFS, "COMSEEK status 0x%x", status));
-
- if (status < 0)
- return -EIO;
- return 0;
-}
-
-
-#ifdef MULTISESSION
-static int cdrommultisession(void __user *arg)
-{
- struct cdrom_multisession ms;
-
- if (copy_from_user(&ms, arg, sizeof ms))
- return -EFAULT;
-
- ms.addr.msf.minute = disk_info.last_session.minute;
- ms.addr.msf.second = disk_info.last_session.second;
- ms.addr.msf.frame = disk_info.last_session.frame;
-
- if (ms.addr_format != CDROM_LBA
- && ms.addr_format != CDROM_MSF)
- return -EINVAL;
- if (ms.addr_format == CDROM_LBA)
- msf2lba(&ms.addr);
-
- ms.xa_flag = disk_info.xa;
-
- if (copy_to_user(arg, &ms, sizeof(struct cdrom_multisession)))
- return -EFAULT;
-
-#if DEBUG_MULTIS
- if (ms.addr_format == CDROM_MSF)
- printk(KERN_DEBUG
- "optcd: multisession xa:%d, msf:%02d:%02d.%02d\n",
- ms.xa_flag,
- ms.addr.msf.minute,
- ms.addr.msf.second,
- ms.addr.msf.frame);
- else
- printk(KERN_DEBUG
- "optcd: multisession %d, lba:0x%08x [%02d:%02d.%02d])\n",
- ms.xa_flag,
- ms.addr.lba,
- disk_info.last_session.minute,
- disk_info.last_session.second,
- disk_info.last_session.frame);
-#endif /* DEBUG_MULTIS */
-
- return 0;
-}
-#endif /* MULTISESSION */
-
-
-static int cdromreset(void)
-{
- if (state != S_IDLE) {
- error = 1;
- tries = 0;
- }
-
- toc_uptodate = 0;
- disk_changed = 1;
- opt_invalidate_buffers();
- audio_status = CDROM_AUDIO_NO_STATUS;
-
- if (!reset_drive())
- return -EIO;
- return 0;
-}
-
-/* VFS calls */
-
-
-static int opt_ioctl(struct inode *ip, struct file *fp,
- unsigned int cmd, unsigned long arg)
-{
- int status, err, retval = 0;
- void __user *argp = (void __user *)arg;
-
- DEBUG((DEBUG_VFS, "starting opt_ioctl"));
-
- if (!ip)
- return -EINVAL;
-
- if (cmd == CDROMRESET)
- return cdromreset();
-
- /* is do_optcd_request or another ioctl busy? */
- if (state != S_IDLE || in_vfs)
- return -EBUSY;
-
- in_vfs = 1;
-
- status = drive_status();
- if (status < 0) {
- DEBUG((DEBUG_VFS, "drive_status: %02x", -status));
- in_vfs = 0;
- return -EIO;
- }
-
- if (status & ST_DOOR_OPEN)
- switch (cmd) { /* Actions that can be taken with door open */
- case CDROMCLOSETRAY:
- /* We do this before trying to read the toc. */
- err = exec_cmd(COMCLOSE);
- if (err < 0) {
- DEBUG((DEBUG_VFS,
- "exec_cmd COMCLOSE: %02x", -err));
- in_vfs = 0;
- return -EIO;
- }
- break;
- default: in_vfs = 0;
- return -EBUSY;
- }
-
- err = update_toc();
- if (err < 0) {
- DEBUG((DEBUG_VFS, "update_toc: %02x", -err));
- in_vfs = 0;
- return -EIO;
- }
-
- DEBUG((DEBUG_VFS, "ioctl cmd 0x%x", cmd));
-
- switch (cmd) {
- case CDROMPAUSE: retval = cdrompause(); break;
- case CDROMRESUME: retval = cdromresume(); break;
- case CDROMPLAYMSF: retval = cdromplaymsf(argp); break;
- case CDROMPLAYTRKIND: retval = cdromplaytrkind(argp); break;
- case CDROMREADTOCHDR: retval = cdromreadtochdr(argp); break;
- case CDROMREADTOCENTRY: retval = cdromreadtocentry(argp); break;
-
- case CDROMSTOP: err = exec_cmd(COMSTOP);
- if (err < 0) {
- DEBUG((DEBUG_VFS,
- "exec_cmd COMSTOP: %02x",
- -err));
- retval = -EIO;
- } else
- audio_status = CDROM_AUDIO_NO_STATUS;
- break;
- case CDROMSTART: break; /* This is a no-op */
- case CDROMEJECT: err = exec_cmd(COMUNLOCK);
- if (err < 0) {
- DEBUG((DEBUG_VFS,
- "exec_cmd COMUNLOCK: %02x",
- -err));
- retval = -EIO;
- break;
- }
- err = exec_cmd(COMOPEN);
- if (err < 0) {
- DEBUG((DEBUG_VFS,
- "exec_cmd COMOPEN: %02x",
- -err));
- retval = -EIO;
- }
- break;
-
- case CDROMVOLCTRL: retval = cdromvolctrl(argp); break;
- case CDROMSUBCHNL: retval = cdromsubchnl(argp); break;
-
- /* The drive detects the mode and automatically delivers the
- correct 2048 bytes, so we don't need these IOCTLs */
- case CDROMREADMODE2: retval = -EINVAL; break;
- case CDROMREADMODE1: retval = -EINVAL; break;
-
- /* Drive doesn't support reading audio */
- case CDROMREADAUDIO: retval = -EINVAL; break;
-
- case CDROMEJECT_SW: auto_eject = (char) arg;
- break;
-
-#ifdef MULTISESSION
- case CDROMMULTISESSION: retval = cdrommultisession(argp); break;
-#endif
-
- case CDROM_GET_MCN: retval = -EINVAL; break; /* not implemented */
- case CDROMVOLREAD: retval = -EINVAL; break; /* not implemented */
-
- case CDROMREADRAW:
- /* this drive delivers 2340 bytes in raw mode */
- retval = cdromread(argp, CD_FRAMESIZE_RAW1, COMREADRAW);
- break;
- case CDROMREADCOOKED:
- retval = cdromread(argp, CD_FRAMESIZE, COMREAD);
- break;
- case CDROMREADALL:
- retval = cdromread(argp, CD_FRAMESIZE_RAWER, COMREADALL);
- break;
-
- case CDROMSEEK: retval = cdromseek(argp); break;
- case CDROMPLAYBLK: retval = -EINVAL; break; /* not implemented */
- case CDROMCLOSETRAY: break; /* The action was taken earlier */
- default: retval = -EINVAL;
- }
- in_vfs = 0;
- return retval;
-}
-
-
-static int open_count = 0;
-
-/* Open device special file; check that a disk is in. */
-static int opt_open(struct inode *ip, struct file *fp)
-{
- DEBUG((DEBUG_VFS, "starting opt_open"));
-
- if (!open_count && state == S_IDLE) {
- int status;
- char *buf;
-
- buf = kmalloc(CD_FRAMESIZE_RAWER, GFP_KERNEL);
- if (!buf) {
- printk(KERN_INFO "optcd: cannot allocate read buffer\n");
- return -ENOMEM;
- }
- optcd_disk->private_data = buf; /* save read buffer */
-
- toc_uptodate = 0;
- opt_invalidate_buffers();
-
- status = exec_cmd(COMCLOSE); /* close door */
- if (status < 0) {
- DEBUG((DEBUG_VFS, "exec_cmd COMCLOSE: %02x", -status));
- }
-
- status = drive_status();
- if (status < 0) {
- DEBUG((DEBUG_VFS, "drive_status: %02x", -status));
- goto err_out;
- }
- DEBUG((DEBUG_VFS, "status: %02x", status));
- if ((status & ST_DOOR_OPEN) || (status & ST_DRVERR)) {
- printk(KERN_INFO "optcd: no disk or door open\n");
- goto err_out;
- }
- status = exec_cmd(COMLOCK); /* Lock door */
- if (status < 0) {
- DEBUG((DEBUG_VFS, "exec_cmd COMLOCK: %02x", -status));
- }
- status = update_toc(); /* Read table of contents */
- if (status < 0) {
- DEBUG((DEBUG_VFS, "update_toc: %02x", -status));
- status = exec_cmd(COMUNLOCK); /* Unlock door */
- if (status < 0) {
- DEBUG((DEBUG_VFS,
- "exec_cmd COMUNLOCK: %02x", -status));
- }
- goto err_out;
- }
- open_count++;
- }
-
- DEBUG((DEBUG_VFS, "exiting opt_open"));
-
- return 0;
-
-err_out:
- return -EIO;
-}
-
-
-/* Release device special file; flush all blocks from the buffer cache */
-static int opt_release(struct inode *ip, struct file *fp)
-{
- int status;
-
- DEBUG((DEBUG_VFS, "executing opt_release"));
- DEBUG((DEBUG_VFS, "inode: %p, device: %s, file: %p\n",
- ip, ip->i_bdev->bd_disk->disk_name, fp));
-
- if (!--open_count) {
- toc_uptodate = 0;
- opt_invalidate_buffers();
- status = exec_cmd(COMUNLOCK); /* Unlock door */
- if (status < 0) {
- DEBUG((DEBUG_VFS, "exec_cmd COMUNLOCK: %02x", -status));
- }
- if (auto_eject) {
- status = exec_cmd(COMOPEN);
- DEBUG((DEBUG_VFS, "exec_cmd COMOPEN: %02x", -status));
- }
- kfree(optcd_disk->private_data);
- del_timer(&delay_timer);
- del_timer(&req_timer);
- }
- return 0;
-}
-
-
-/* Check if disk has been changed */
-static int opt_media_change(struct gendisk *disk)
-{
- DEBUG((DEBUG_VFS, "executing opt_media_change"));
- DEBUG((DEBUG_VFS, "dev: %s; disk_changed = %d\n",
- disk->disk_name, disk_changed));
-
- if (disk_changed) {
- disk_changed = 0;
- return 1;
- }
- return 0;
-}
-
-/* Driver initialisation */
-
-
-/* Returns 1 if a drive is detected with a version string
- starting with "DOLPHIN". Otherwise 0. */
-static int __init version_ok(void)
-{
- char devname[100];
- int count, i, ch, status;
-
- status = exec_cmd(COMVERSION);
- if (status < 0) {
- DEBUG((DEBUG_VFS, "exec_cmd COMVERSION: %02x", -status));
- return 0;
- }
- if ((count = get_data(1)) < 0) {
- DEBUG((DEBUG_VFS, "get_data(1): %02x", -count));
- return 0;
- }
- for (i = 0, ch = -1; count > 0; count--) {
- if ((ch = get_data(1)) < 0) {
- DEBUG((DEBUG_VFS, "get_data(1): %02x", -ch));
- break;
- }
- if (i < 99)
- devname[i++] = ch;
- }
- devname[i] = '\0';
- if (ch < 0)
- return 0;
-
- printk(KERN_INFO "optcd: Device %s detected\n", devname);
- return ((devname[0] == 'D')
- && (devname[1] == 'O')
- && (devname[2] == 'L')
- && (devname[3] == 'P')
- && (devname[4] == 'H')
- && (devname[5] == 'I')
- && (devname[6] == 'N'));
-}
-
-
-static struct block_device_operations opt_fops = {
- .owner = THIS_MODULE,
- .open = opt_open,
- .release = opt_release,
- .ioctl = opt_ioctl,
- .media_changed = opt_media_change,
-};
-
-#ifndef MODULE
-/* Get kernel parameter when used as a kernel driver */
-static int optcd_setup(char *str)
-{
- int ints[4];
- (void)get_options(str, ARRAY_SIZE(ints), ints);
-
- if (ints[0] > 0)
- optcd_port = ints[1];
-
- return 1;
-}
-
-__setup("optcd=", optcd_setup);
-
-#endif /* MODULE */
-
-/* Test for presence of drive and initialize it. Called at boot time
- or during module initialisation. */
-static int __init optcd_init(void)
-{
- int status;
-
- if (optcd_port <= 0) {
- printk(KERN_INFO
- "optcd: no Optics Storage CDROM Initialization\n");
- return -EIO;
- }
- optcd_disk = alloc_disk(1);
- if (!optcd_disk) {
- printk(KERN_ERR "optcd: can't allocate disk\n");
- return -ENOMEM;
- }
- optcd_disk->major = MAJOR_NR;
- optcd_disk->first_minor = 0;
- optcd_disk->fops = &opt_fops;
- sprintf(optcd_disk->disk_name, "optcd");
-
- if (!request_region(optcd_port, 4, "optcd")) {
- printk(KERN_ERR "optcd: conflict, I/O port 0x%x already used\n",
- optcd_port);
- put_disk(optcd_disk);
- return -EIO;
- }
-
- if (!reset_drive()) {
- printk(KERN_ERR "optcd: drive at 0x%x not ready\n", optcd_port);
- release_region(optcd_port, 4);
- put_disk(optcd_disk);
- return -EIO;
- }
- if (!version_ok()) {
- printk(KERN_ERR "optcd: unknown drive detected; aborting\n");
- release_region(optcd_port, 4);
- put_disk(optcd_disk);
- return -EIO;
- }
- status = exec_cmd(COMINITDOUBLE);
- if (status < 0) {
- printk(KERN_ERR "optcd: cannot init double speed mode\n");
- release_region(optcd_port, 4);
- DEBUG((DEBUG_VFS, "exec_cmd COMINITDOUBLE: %02x", -status));
- put_disk(optcd_disk);
- return -EIO;
- }
- if (register_blkdev(MAJOR_NR, "optcd")) {
- release_region(optcd_port, 4);
- put_disk(optcd_disk);
- return -EIO;
- }
-
-
- opt_queue = blk_init_queue(do_optcd_request, &optcd_lock);
- if (!opt_queue) {
- unregister_blkdev(MAJOR_NR, "optcd");
- release_region(optcd_port, 4);
- put_disk(optcd_disk);
- return -ENOMEM;
- }
-
- blk_queue_hardsect_size(opt_queue, 2048);
- optcd_disk->queue = opt_queue;
- add_disk(optcd_disk);
-
- printk(KERN_INFO "optcd: DOLPHIN 8000 AT CDROM at 0x%x\n", optcd_port);
- return 0;
-}
-
-
-static void __exit optcd_exit(void)
-{
- del_gendisk(optcd_disk);
- put_disk(optcd_disk);
- if (unregister_blkdev(MAJOR_NR, "optcd") == -EINVAL) {
- printk(KERN_ERR "optcd: what's that: can't unregister\n");
- return;
- }
- blk_cleanup_queue(opt_queue);
- release_region(optcd_port, 4);
- printk(KERN_INFO "optcd: module released.\n");
-}
-
-module_init(optcd_init);
-module_exit(optcd_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_BLOCKDEV_MAJOR(OPTICS_CDROM_MAJOR);