⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cdu31a.c

📁 cdrom device drive for linux.
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
* 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 + -