📄 cdu31a.c
字号:
/*
* Sony CDU-31A CDROM interface device driver.
*
* Corey Minyard (minyard@wf-rch.cirr.com)
*
* Colossians 3:17
*
* See Documentation/cdrom/cdu31a for additional details about this driver.
*
* The Sony interface device driver handles Sony interface CDROM
* drives and provides a complete block-level interface as well as an
* ioctl() interface compatible with the Sun (as specified in
* include/linux/cdrom.h). With this interface, CDROMs can be
* accessed and standard audio CDs can be played back normally.
*
* WARNING - All autoprobes have been removed from the driver.
* You MUST configure the CDU31A via a LILO config
* at boot time or in lilo.conf. I have the
* following in my lilo.conf:
*
* append="cdu31a=0x1f88,0,PAS"
*
* The first number is the I/O base address of the
* card. The second is the interrupt (0 means none).
* The third should be "PAS" if on a Pro-Audio
* spectrum, or nothing if on something else.
*
* This interface is (unfortunately) a polled interface. This is
* because most Sony interfaces are set up with DMA and interrupts
* disables. Some (like mine) do not even have the capability to
* handle interrupts or DMA. For this reason you will see a lot of
* the following:
*
* retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
* while (time_before(jiffies, retry_count) && (! <some condition to wait for))
* {
* while (handle_sony_cd_attention())
* ;
*
* sony_sleep();
* }
* if (the condition not met)
* {
* return an error;
* }
*
* This ugly hack waits for something to happen, sleeping a little
* between every try. it also handles attentions, which are
* asynchronous events from the drive informing the driver that a disk
* has been inserted, removed, etc.
*
* NEWS FLASH - The driver now supports interrupts but they are
* turned off by default. Use of interrupts is highly encouraged, it
* cuts CPU usage down to a reasonable level. I had DMA in for a while
* but PC DMA is just too slow. Better to just insb() it.
*
* One thing about these drives: They talk in MSF (Minute Second Frame) format.
* There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
* disk. The funny thing is that these are sent to the drive in BCD, but the
* interface wants to see them in decimal. A lot of conversion goes on.
*
* DRIVER SPECIAL FEATURES
* -----------------------
*
* This section describes features beyond the normal audio and CD-ROM
* functions of the drive.
*
* 2048 byte buffer mode
*
* If a disk is mounted with -o block=2048, data is copied straight
* from the drive data port to the buffer. Otherwise, the readahead
* buffer must be involved to hold the other 1K of data when a 1K
* block operation is done. Note that with 2048 byte blocks you
* cannot execute files from the CD.
*
* XA compatibility
*
* The driver should support XA disks for both the CDU31A and CDU33A.
* It does this transparently, the using program doesn't need to set it.
*
* Multi-Session
*
* A multi-session disk looks just like a normal disk to the user.
* Just mount one normally, and all the data should be there.
* A special thanks to Koen for help with this!
*
* Raw sector I/O
*
* Using the CDROMREADAUDIO it is possible to read raw audio and data
* tracks. Both operations return 2352 bytes per sector. On the data
* tracks, the first 12 bytes is not returned by the drive and the value
* of that data is indeterminate.
*
*
* Copyright (C) 1993 Corey Minyard
*
* 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 of the License, 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.
*
* TODO:
* CDs with form1 and form2 sectors cause problems
* with current read-ahead strategy.
*
* Credits:
* Heiko Eissfeldt <heiko@colossus.escape.de>
* For finding abug in the return of the track numbers.
* TOC processing redone for proper multisession support.
*
*
* It probably a little late to be adding a history, but I guess I
* will start.
*
* 10/24/95 - Added support for disabling the eject button when the
* drive is open. Note that there is a small problem
* still here, if the eject button is pushed while the
* drive light is flashing, the drive will return a bad
* status and be reset. It recovers, though.
*
* 03/07/97 - Fixed a problem with timers.
*
*
* 18 Spetember 1997 -- Ported to Uniform CD-ROM driver by
* Heiko Eissfeldt <heiko@colossus.escape.de> with additional
* changes by Erik Andersen <andersee@debian.org>
*
* 24 January 1998 -- Removed the scd_disc_status() function, which was now
* just dead code left over from the port.
* Erik Andersen <andersee@debian.org>
*
* 16 July 1998 -- Drive donated to Erik Andersen by John Kodis
* <kodis@jagunet.com>. Work begun on fixing driver to
* work under 2.1.X. Added temporary extra printks
* which seem to slow it down enough to work.
*
* 9 November 1999 -- 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>
*/
#include <linux/major.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/ioport.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/dma.h>
#include <linux/cdrom.h>
#include "cdu31a.h"
#define MAJOR_NR CDU31A_CDROM_MAJOR
#include <linux/blk.h>
#define CDU31A_READAHEAD 4 /* 128 sector, 64kB, 32 reads read-ahead */
#define CDU31A_MAX_CONSECUTIVE_ATTENTIONS 10
#define DEBUG 0
/* Define the following if you have data corruption problems. */
#undef SONY_POLL_EACH_BYTE
/*
** Edit the following data to change interrupts, DMA channels, etc.
** Default is polled and no DMA. DMA is not recommended for double-speed
** drives.
*/
static struct {
unsigned short base; /* I/O Base Address */
short int_num; /* Interrupt Number (-1 means scan for it,
0 means don't use) */
} cdu31a_addresses[] __initdata = {
{0}
};
static int handle_sony_cd_attention(void);
static int read_subcode(void);
static void sony_get_toc(void);
static int scd_spinup(void);
/*static int scd_open(struct inode *inode, struct file *filp);*/
static int scd_open(struct cdrom_device_info *, int);
static void do_sony_cd_cmd(unsigned char cmd,
unsigned char *params,
unsigned int num_params,
unsigned char *result_buffer,
unsigned int *result_size);
static void size_to_buf(unsigned int size, unsigned char *buf);
/* Parameters for the read-ahead. */
static unsigned int sony_next_block; /* Next 512 byte block offset */
static unsigned int sony_blocks_left = 0; /* Number of 512 byte blocks left
in the current read command. */
/* The base I/O address of the Sony Interface. This is a variable (not a
#define) so it can be easily changed via some future ioctl() */
static unsigned int cdu31a_port = 0;
MODULE_PARM(cdu31a_port, "i");
/*
* The following are I/O addresses of the various registers for the drive. The
* comment for the base address also applies here.
*/
static volatile unsigned short sony_cd_cmd_reg;
static volatile unsigned short sony_cd_param_reg;
static volatile unsigned short sony_cd_write_reg;
static volatile unsigned short sony_cd_control_reg;
static volatile unsigned short sony_cd_status_reg;
static volatile unsigned short sony_cd_result_reg;
static volatile unsigned short sony_cd_read_reg;
static volatile unsigned short sony_cd_fifost_reg;
static int sony_spun_up = 0; /* Has the drive been spun up? */
static int sony_speed = 0; /* Last wanted speed */
static int sony_xa_mode = 0; /* Is an XA disk in the drive
and the drive a CDU31A? */
static int sony_raw_data_mode = 1; /* 1 if data tracks, 0 if audio.
For raw data reads. */
static unsigned int sony_usage = 0; /* How many processes have the
drive open. */
static int sony_pas_init = 0; /* Initialize the Pro-Audio
Spectrum card? */
static struct s_sony_session_toc single_toc; /* Holds the
table of
contents. */
static struct s_all_sessions_toc sony_toc; /* entries gathered from all
sessions */
static int sony_toc_read = 0; /* Has the TOC been read for
the drive? */
static struct s_sony_subcode last_sony_subcode; /* Points to the last
subcode address read */
static volatile int sony_inuse = 0; /* Is the drive in use? Only one operation
at a time allowed */
static DECLARE_WAIT_QUEUE_HEAD(sony_wait); /* Things waiting for the drive */
static struct task_struct *has_cd_task = NULL; /* The task that is currently
using the CDROM drive, or
NULL if none. */
static int is_double_speed = 0; /* does the drive support double speed ? */
static int is_a_cdu31a = 1; /* Is the drive a CDU31A? */
static int is_auto_eject = 1; /* Door has been locked? 1=No/0=Yes */
/*
* The audio status uses the values from read subchannel data as specified
* in include/linux/cdrom.h.
*/
static volatile int sony_audio_status = CDROM_AUDIO_NO_STATUS;
/*
* The following are a hack for pausing and resuming audio play. The drive
* does not work as I would expect it, if you stop it then start it again,
* the drive seeks back to the beginning and starts over. This holds the
* position during a pause so a resume can restart it. It uses the
* audio status variable above to tell if it is paused.
*/
static unsigned volatile char cur_pos_msf[3] = { 0, 0, 0 };
static unsigned volatile char final_pos_msf[3] = { 0, 0, 0 };
/* What IRQ is the drive using? 0 if none. */
static int cdu31a_irq = 0;
MODULE_PARM(cdu31a_irq, "i");
/* The interrupt handler will wake this queue up when it gets an
interrupts. */
DECLARE_WAIT_QUEUE_HEAD(cdu31a_irq_wait);
static int curr_control_reg = 0; /* Current value of the control register */
/* A disk changed variable. When a disk change is detected, it will
all be set to TRUE. As the upper layers ask for disk_changed status
it will be cleared. */
static char disk_changed;
/* Variable for using the readahead buffer. The readahead buffer
is used for raw sector reads and for blocksizes that are smaller
than 2048 bytes. */
static char readahead_buffer[CD_FRAMESIZE_RAW];
static int readahead_dataleft = 0;
static int readahead_bad = 0;
/* Used to time a short period to abort an operation after the
drive has been idle for a while. This keeps the light on
the drive from flashing for very long. */
static struct timer_list cdu31a_abort_timer;
/* Marks if the timeout has started an abort read. This is used
on entry to the drive to tell the code to read out the status
from the abort read. */
static int abort_read_started = 0;
/*
* This routine returns 1 if the disk has been changed since the last
* check or 0 if it hasn't.
*/
static int scd_disk_change(kdev_t full_dev)
{
int retval;
retval = disk_changed;
disk_changed = 0;
return retval;
}
/*
* Uniform cdrom interface function
* report back, if disc has changed from time of last request.
*/
static int scd_media_changed(struct cdrom_device_info *cdi, int disc_nr)
{
return scd_disk_change(cdi->dev);
}
/*
* Uniform cdrom interface function
* report back, if drive is ready
*/
static int scd_drive_status(struct cdrom_device_info *cdi, int slot_nr)
{
if (CDSL_CURRENT != slot_nr) {
/* we have no changer support */
return -EINVAL;
}
if (scd_spinup() == 0) {
sony_spun_up = 1;
}
return sony_spun_up ? CDS_DISC_OK : CDS_DRIVE_NOT_READY;
}
static inline void enable_interrupts(void)
{
curr_control_reg |= (SONY_ATTN_INT_EN_BIT
| SONY_RES_RDY_INT_EN_BIT
| SONY_DATA_RDY_INT_EN_BIT);
outb(curr_control_reg, sony_cd_control_reg);
}
static inline void disable_interrupts(void)
{
curr_control_reg &= ~(SONY_ATTN_INT_EN_BIT
| SONY_RES_RDY_INT_EN_BIT
| SONY_DATA_RDY_INT_EN_BIT);
outb(curr_control_reg, sony_cd_control_reg);
}
/*
* Wait a little while (used for polling the drive). If in initialization,
* setting a timeout doesn't work, so just loop for a while.
*/
static inline void sony_sleep(void)
{
unsigned long flags;
if (cdu31a_irq <= 0) {
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(0);
} else { /* Interrupt driven */
save_flags(flags);
cli();
enable_interrupts();
interruptible_sleep_on(&cdu31a_irq_wait);
restore_flags(flags);
}
}
/*
* The following are convenience routine to read various status and set
* various conditions in the drive.
*/
static inline int is_attention(void)
{
return ((inb(sony_cd_status_reg) & SONY_ATTN_BIT) != 0);
}
static inline int is_busy(void)
{
return ((inb(sony_cd_status_reg) & SONY_BUSY_BIT) != 0);
}
static inline int is_data_ready(void)
{
return ((inb(sony_cd_status_reg) & SONY_DATA_RDY_BIT) != 0);
}
static inline int is_data_requested(void)
{
return ((inb(sony_cd_status_reg) & SONY_DATA_REQUEST_BIT) != 0);
}
static inline int is_result_ready(void)
{
return ((inb(sony_cd_status_reg) & SONY_RES_RDY_BIT) != 0);
}
static inline int is_param_write_rdy(void)
{
return ((inb(sony_cd_fifost_reg) & SONY_PARAM_WRITE_RDY_BIT) != 0);
}
static inline int is_result_reg_not_empty(void)
{
return ((inb(sony_cd_fifost_reg) & SONY_RES_REG_NOT_EMP_BIT) != 0);
}
static inline void reset_drive(void)
{
curr_control_reg = 0;
readahead_dataleft = 0;
sony_toc_read = 0;
outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg);
}
/*
* Uniform cdrom interface function
* reset drive and return when it is ready
*/
static int scd_reset(struct cdrom_device_info *cdi)
{
int retry_count;
reset_drive();
retry_count = jiffies + SONY_RESET_TIMEOUT;
while (time_before(jiffies, retry_count) && (!is_attention())) {
sony_sleep();
}
return 0;
}
static inline void clear_attention(void)
{
outb(curr_control_reg | SONY_ATTN_CLR_BIT, sony_cd_control_reg);
}
static inline void clear_result_ready(void)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -