optcd.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:50k
- /* 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 <linux/devfs_fs_kernel.h>
- #include <asm/io.h>
- #define MAJOR_NR OPTICS_CDROM_MAJOR
- #include <linux/blk.h>
- #include <linux/cdrom.h>
- #include "optcd.h"
- #include <asm/uaccess.h>
- /* 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);
- vsprintf(s, fmt, args);
- printk(KERN_DEBUG "optcd: %sn", s);
- va_end(args);
- }
- #else
- #define DEBUG(x)
- #endif
- static int blksize = 2048;
- static int hsecsize = 2048;
- /* 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_PARM(optcd_port, "h");
- /* 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. */
- inline static 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 struct timer_list delay_timer = {function: sleep_timer};
- /* 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%02xn", 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. */
- inline static 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. */
- inline static 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 */
- inline static int stdt_flags(void)
- {
- return inb(STATUS_PORT) & FL_STDT;
- }
- /* Fetch status that has previously been waited for. <0 means not available */
- inline static 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. */
- inline static 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 */
- inline static 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 */
- inline static 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. */
- inline static 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 */
- inline static 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 */
- inline static 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) */
- inline static 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 */
- inline static 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
- void toc_debug_info(int i)
- {
- printk(KERN_DEBUG "#%3d ctl %1x, adr %1x, track %2d index %3d"
- " %2d:%02d.%02d %2d:%02d.%02dn",
- 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 linux/Documentation/cdrom/optcdn");
- DEBUG((DEBUG_TOC, "exiting update_toc"));
- toc_uptodate = 1;
- return 0;
- }
- /* Request handling */
- #define CURRENT_VALID
- (!QUEUE_EMPTY && MAJOR(CURRENT -> rq_dev) == MAJOR_NR
- && CURRENT -> cmd == 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;
- inline static 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 transfern");
- #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%02xn", error);
- opt_invalidate_buffers();
- if (!tries--) {
- printk(KERN_ERR "optcd: read block %d failed;"
- " Giving upn", next_bn);
- if (transfer_is_active)
- loop_again = 0;
- if (CURRENT_VALID)
- end_request(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 staten", state_n);
- printk(KERN_DEBUG "optcd: state %dn", 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(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: %sn",
- (status & ST_DOOR_OPEN)
- ? "door open"
- : "disk removed");
- state = S_IDLE;
- while (CURRENT_VALID)
- end_request(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:%xn", flags);
- }
- if (flags == FL_STEN)
- printk(KERN_DEBUG "timeout cnt: %dn", timeout);
- #endif
- switch (flags) {
- case FL_DTEN: /* only STEN low */
- if (!tries--) {
- printk(KERN_ERR
- "optcd: read block %d failed; "
- "Giving upn", next_bn);
- if (transfer_is_active) {
- tries = 0;
- break;
- }
- if (CURRENT_VALID)
- end_request(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 framesn");
- 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:%dn",
- read_count,
- CURRENT->nr_sectors,
- buf_in);
- printk(KERN_ERR
- "transfer active: %xn",
- transfer_is_active);
- read_count = 0;
- state = S_STOP;
- loop_again = 1;
- end_request(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(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 framesn",
- read_count);
- flush_data();
- if (send_cmd(COMDRVST)) {
- state = S_IDLE;
- while (CURRENT_VALID)
- end_request(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 %dn", state);
- return;
- } /* case */
- } /* while */
- if (!timeout--) {
- printk(KERN_ERR "optcd: timeout in state %dn", state);
- state = S_STOP;
- if (exec_cmd(COMSTOP) < 0) {
- state = S_IDLE;
- while (CURRENT_VALID)
- end_request(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 CDn");
- end_request(0);
- return;
- }
- transfer_is_active = 1;
- while (CURRENT_VALID) {
- if (CURRENT->bh) {
- if (!buffer_locked(CURRENT->bh))
- panic(DEVICE_NAME ": block not locked");
- }
- transfer(); /* First try to transfer block from buffers */
- if (CURRENT -> nr_sectors == 0) {
- end_request(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(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(unsigned long arg)
- {
- int status;
- struct cdrom_msf msf;
- status = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
- if (status)
- return status;
- copy_from_user(&msf, (void *) arg, sizeof msf);
- 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(unsigned long arg)
- {
- int status;
- struct cdrom_ti ti;
- struct cdrom_msf msf;
- status = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
- if (status)
- return status;
- copy_from_user(&ti, (void *) arg, sizeof ti);
- 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(unsigned long arg)
- {
- int status;
- struct cdrom_tochdr tochdr;
- status = verify_area(VERIFY_WRITE, (void *) arg, sizeof tochdr);
- if (status)
- return status;
- tochdr.cdth_trk0 = disk_info.first;
- tochdr.cdth_trk1 = disk_info.last;
- copy_to_user((void *) arg, &tochdr, sizeof tochdr);
- return 0;
- }
- static int cdromreadtocentry(unsigned long arg)
- {
- int status;
- struct cdrom_tocentry entry;
- struct cdrom_subchnl *tocptr;
- status = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
- if (status)
- return status;
- copy_from_user(&entry, (void *) arg, sizeof entry);
- 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;
- copy_to_user((void *) arg, &entry, sizeof entry);
- return 0;
- }
- static int cdromvolctrl(unsigned long arg)
- {
- int status;
- struct cdrom_volctrl volctrl;
- struct cdrom_msf msf;
- status = verify_area(VERIFY_READ, (void *) arg, sizeof volctrl);
- if (status)
- return status;
- copy_from_user(&volctrl, (char *) arg, sizeof volctrl);
- 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(unsigned long arg)
- {
- int status;
- struct cdrom_subchnl subchnl;
- status = verify_area(VERIFY_WRITE, (void *) arg, sizeof subchnl);
- if (status)
- return status;
- copy_from_user(&subchnl, (void *) arg, sizeof subchnl);
- 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;
- }
- copy_to_user((void *) arg, &subchnl, sizeof subchnl);
- return 0;
- }
- static int cdromread(unsigned long arg, int blocksize, int cmd)
- {
- int status;
- struct cdrom_msf msf;
- char buf[CD_FRAMESIZE_RAWER];
- status = verify_area(VERIFY_WRITE, (void *) arg, blocksize);
- if (status)
- return status;
- copy_from_user(&msf, (void *) arg, sizeof msf);
- 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(buf, blocksize);
- copy_to_user((void *) arg, &buf, blocksize);
- return 0;
- }
- static int cdromseek(unsigned long arg)
- {
- int status;
- struct cdrom_msf msf;
- status = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
- if (status)
- return status;
- copy_from_user(&msf, (void *) arg, sizeof msf);
- 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(unsigned long arg)
- {
- int status;
- struct cdrom_multisession ms;
- status = verify_area(VERIFY_WRITE, (void*) arg, sizeof ms);
- if (status)
- return status;
- copy_from_user(&ms, (void*) arg, sizeof ms);
- 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;
- copy_to_user((void*) arg, &ms,
- sizeof(struct cdrom_multisession));
- #if DEBUG_MULTIS
- if (ms.addr_format == CDROM_MSF)
- printk(KERN_DEBUG
- "optcd: multisession xa:%d, msf:%02d:%02d.%02dn",
- 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;
- 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(arg); break;
- case CDROMPLAYTRKIND: retval = cdromplaytrkind(arg); break;
- case CDROMREADTOCHDR: retval = cdromreadtochdr(arg); break;
- case CDROMREADTOCENTRY: retval = cdromreadtocentry(arg); 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(arg); break;
- case CDROMSUBCHNL: retval = cdromsubchnl(arg); 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(arg); 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(arg, CD_FRAMESIZE_RAW1, COMREADRAW);
- break;
- case CDROMREADCOOKED:
- retval = cdromread(arg, CD_FRAMESIZE, COMREAD);
- break;
- case CDROMREADALL:
- retval = cdromread(arg, CD_FRAMESIZE_RAWER, COMREADALL);
- break;
- case CDROMSEEK: retval = cdromseek(arg); 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;
- 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 openn");
- 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, inode -> i_rdev: 0x%x, file: %pn",
- ip, ip -> i_rdev, 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));
- }
- del_timer(&delay_timer);
- del_timer(&req_timer);
- }
- return 0;
- }
- /* Check if disk has been changed */
- static int opt_media_change(kdev_t dev)
- {
- DEBUG((DEBUG_VFS, "executing opt_media_change"));
- DEBUG((DEBUG_VFS, "dev: 0x%x; disk_changed = %dn", dev, 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] = ' ';
- if (ch < 0)
- return 0;
- printk(KERN_INFO "optcd: Device %s detectedn", 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,
- check_media_change: 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. */
- int __init optcd_init(void)
- {
- int status;
- if (optcd_port <= 0) {
- printk(KERN_INFO
- "optcd: no Optics Storage CDROM Initializationn");
- return -EIO;
- }
- if (check_region(optcd_port, 4)) {
- printk(KERN_ERR "optcd: conflict, I/O port 0x%x already usedn",
- optcd_port);
- return -EIO;
- }
- if (!reset_drive()) {
- printk(KERN_ERR "optcd: drive at 0x%x not readyn", optcd_port);
- return -EIO;
- }
- if (!version_ok()) {
- printk(KERN_ERR "optcd: unknown drive detected; abortingn");
- return -EIO;
- }
- status = exec_cmd(COMINITDOUBLE);
- if (status < 0) {
- printk(KERN_ERR "optcd: cannot init double speed moden");
- DEBUG((DEBUG_VFS, "exec_cmd COMINITDOUBLE: %02x", -status));
- return -EIO;
- }
- if (devfs_register_blkdev(MAJOR_NR, "optcd", &opt_fops) != 0)
- {
- printk(KERN_ERR "optcd: unable to get major %dn", MAJOR_NR);
- return -EIO;
- }
- devfs_register (NULL, "optcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0,
- S_IFBLK | S_IRUGO | S_IWUGO, &opt_fops, NULL);
- hardsect_size[MAJOR_NR] = &hsecsize;
- blksize_size[MAJOR_NR] = &blksize;
- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
- read_ahead[MAJOR_NR] = 4;
- request_region(optcd_port, 4, "optcd");
- register_disk(NULL, MKDEV(MAJOR_NR,0), 1, &opt_fops, 0);
- printk(KERN_INFO "optcd: DOLPHIN 8000 AT CDROM at 0x%xn", optcd_port);
- return 0;
- }
- void __exit optcd_exit(void)
- {
- devfs_unregister(devfs_find_handle(NULL, "optcd", 0, 0,
- DEVFS_SPECIAL_BLK, 0));
- if (devfs_unregister_blkdev(MAJOR_NR, "optcd") == -EINVAL) {
- printk(KERN_ERR "optcd: what's that: can't unregistern");
- return;
- }
- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
- release_region(optcd_port, 4);
- printk(KERN_INFO "optcd: module released.n");
- }
- #ifdef MODULE
- module_init(optcd_init);
- #endif
- module_exit(optcd_exit);
- MODULE_LICENSE("GPL");