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

📄 sonycd535.c

📁 cdrom device drive for linux.
💻 C
📖 第 1 页 / 共 4 页
字号:
/*
 * Sony CDU-535 interface device driver
 *
 * This is a modified version of the CDU-31A device driver (see below).
 * Changes were made using documentation for the CDU-531 (which Sony
 * assures me is very similar to the 535) and partial disassembly of the
 * DOS driver.  I used Minyard's driver and replaced the CDU-31A
 * commands with the CDU-531 commands.  This was complicated by a different
 * interface protocol with the drive.  The driver is still polled.
 *
 * Data transfer rate is about 110 Kb/sec, theoretical maximum is 150 Kb/sec.
 * I tried polling without the sony_sleep during the data transfers but
 * it did not speed things up any.
 *
 * 1993-05-23 (rgj) changed the major number to 21 to get rid of conflict
 * with CDU-31A driver.  This is the also the number from the Linux
 * Device Driver Registry for the Sony Drive.  Hope nobody else is using it.
 *
 * 1993-08-29 (rgj) remove the configuring of the interface board address
 * from the top level configuration, you have to modify it in this file.
 *
 * 1995-01-26 Made module-capable (Joel Katz <Stimpson@Panix.COM>)
 *
 * 1995-05-20
 *  Modified to support CDU-510/515 series
 *      (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>)
 *  Fixed to report verify_area() failures
 *      (Heiko Eissfeldt <heiko@colossus.escape.de>)
 *
 * 1995-06-01
 *  More changes to support CDU-510/515 series
 *      (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>)
 *
 * 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>
 *
 * Things to do:
 *  - handle errors and status better, put everything into a single word
 *  - use interrupts (code mostly there, but a big hole still missing)
 *  - handle multi-session CDs?
 *  - use DMA?
 *
 *  Known Bugs:
 *  -
 *
 *   Ken Pizzini (ken@halcyon.com)
 *
 * Original by:
 *   Ron Jeppesen (ronj.an@site007.saic.com)
 *
 *
 *------------------------------------------------------------------------
 * Sony CDROM interface device driver.
 *
 * Corey Minyard (minyard@wf-rch.cirr.com) (CDU-535 complaints to Ken above)
 *
 * Colossians 3:17
 *
 * 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.
 *
 * 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 bit of
 * the following:
 *
 *   snap = jiffies;
 *   while (jiffies-snap < SONY_JIFFIES_TIMEOUT)
 *   {
 *		if (some_condition())
 *         break;
 *      sony_sleep();
 *   }
 *   if (some_condition not met)
 *   {
 *      return an_error;
 *   }
 *
 * This ugly hack waits for something to happen, sleeping a little
 * between every try.  (The conditional is written so that jiffies
 * wrap-around is handled properly.)
 *
 * 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.
 *
 *  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.
 *
 */


# 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/ioport.h>
#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/devfs_fs_kernel.h>

#define REALLY_SLOW_IO
#include <asm/system.h>
#include <asm/io.h>
#include <asm/uaccess.h>

#include <linux/cdrom.h>

#define MAJOR_NR CDU535_CDROM_MAJOR
# include <linux/blk.h>
#define sony535_cd_base_io sonycd535 /* for compatible parameter passing with "insmod" */
#include "sonycd535.h"

/*
 * this is the base address of the interface card for the Sony CDU-535
 * CDROM drive.  If your jumpers are set for an address other than
 * this one (the default), change the following line to the
 * proper address.
 */
#ifndef CDU535_ADDRESS
# define CDU535_ADDRESS			0x340
#endif
#ifndef CDU535_INTERRUPT
# define CDU535_INTERRUPT		0
#endif
#ifndef CDU535_HANDLE
# define CDU535_HANDLE			"cdu535"
#endif
#ifndef CDU535_MESSAGE_NAME
# define CDU535_MESSAGE_NAME	"Sony CDU-535"
#endif

#define CDU535_BLOCK_SIZE	2048 
 
#ifndef MAX_SPINUP_RETRY
# define MAX_SPINUP_RETRY		3	/* 1 is sufficient for most drives... */
#endif
#ifndef RETRY_FOR_BAD_STATUS
# define RETRY_FOR_BAD_STATUS	100	/* in 10th of second */
#endif

#ifndef DEBUG
# define DEBUG	1
#endif

/*
 *  SONY535_BUFFER_SIZE determines the size of internal buffer used
 *  by the drive.  It must be at least 2K and the larger the buffer
 *  the better the transfer rate.  It does however take system memory.
 *  On my system I get the following transfer rates using dd to read
 *  10 Mb off /dev/cdrom.
 *
 *    8K buffer      43 Kb/sec
 *   16K buffer      66 Kb/sec
 *   32K buffer      91 Kb/sec
 *   64K buffer     111 Kb/sec
 *  128K buffer     123 Kb/sec
 *  512K buffer     123 Kb/sec
 */
#define SONY535_BUFFER_SIZE	(64*1024)

/*
 *  if LOCK_DOORS is defined then the eject button is disabled while
 * the device is open.
 */
#ifndef NO_LOCK_DOORS
# define LOCK_DOORS
#endif

static int read_subcode(void);
static void sony_get_toc(void);
static int cdu_open(struct inode *inode, struct file *filp);
static inline unsigned int int_to_bcd(unsigned int val);
static unsigned int bcd_to_int(unsigned int bcd);
static int do_sony_cmd(Byte * cmd, int nCmd, Byte status[2],
					   Byte * response, int n_response, int ignoreStatusBit7);

/* 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 sony535_cd_base_io = CDU535_ADDRESS;
MODULE_PARM(sony535_cd_base_io, "i");

/*
 * The following are I/O addresses of the various registers for the drive.  The
 * comment for the base address also applies here.
 */
static unsigned short select_unit_reg;
static unsigned short result_reg;
static unsigned short command_reg;
static unsigned short read_status_reg;
static unsigned short data_reg;

static int initialized;			/* Has the drive been initialized? */
static int sony_disc_changed = 1;	/* Has the disk been changed
					   since the last check? */
static int sony_toc_read;		/* Has the table of contents been
					   read? */
static unsigned int sony_buffer_size;	/* Size in bytes of the read-ahead
					   buffer. */
static unsigned int sony_buffer_sectors;	/* Size (in 2048 byte records) of
						   the read-ahead buffer. */
static unsigned int sony_usage;		/* How many processes have the
					   drive open. */

static int sony_first_block = -1;	/* First OS block (512 byte) in
					   the read-ahead buffer */
static int sony_last_block = -1;	/* Last OS block (512 byte) in
					   the read-ahead buffer */

static struct s535_sony_toc *sony_toc;	/* Points to the table of
					   contents. */

static struct s535_sony_subcode *last_sony_subcode;		/* Points to the last
								   subcode address read */
static Byte **sony_buffer;		/* Points to the pointers
					   to the sector buffers */

static int sony_inuse;			/* is the drive in use? Only one
					   open at a time allowed */

/*
 * The audio status uses the values from read subchannel data as specified
 * in include/linux/cdrom.h.
 */
static 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.
 *   I just kept the CDU-31A driver behavior rather than using the PAUSE
 * command on the CDU-535.
 */
static Byte cur_pos_msf[3];
static Byte final_pos_msf[3];

/* What IRQ is the drive using?  0 if none. */
static int sony535_irq_used = CDU535_INTERRUPT;

/* The interrupt handler will wake this queue up when it gets an interrupt. */
static DECLARE_WAIT_QUEUE_HEAD(cdu535_irq_wait);


/*
 * This routine returns 1 if the disk has been changed since the last
 * check or 0 if it hasn't.  Setting flag to 0 resets the changed flag.
 */
static int
cdu535_check_media_change(kdev_t full_dev)
{
	int retval;

	if (MINOR(full_dev) != 0) {
		printk(CDU535_MESSAGE_NAME " request error: invalid device.\n");
		return 0;
	}

	/* if driver is not initialized, always return 0 */
	retval = initialized ? sony_disc_changed : 0;
	sony_disc_changed = 0;
	return retval;
}

static inline void
enable_interrupts(void)
{
#ifdef USE_IRQ
	/*
	 * This code was taken from cdu31a.c; it will not
	 * directly work for the cdu535 as written...
	 */
	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);
#endif
}

static inline void
disable_interrupts(void)
{
#ifdef USE_IRQ
	/*
	 * This code was taken from cdu31a.c; it will not
	 * directly work for the cdu535 as written...
	 */
	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);
#endif
}

static void
cdu535_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
	disable_interrupts();
	if (waitqueue_active(&cdu535_irq_wait))
		wake_up(&cdu535_irq_wait);
	else
		printk(CDU535_MESSAGE_NAME
				": Got an interrupt but nothing was waiting\n");
}


/*
 * Wait a little while.
 */
static inline void
sony_sleep(void)
{
	if (sony535_irq_used <= 0) {	/* poll */
		current->state = TASK_INTERRUPTIBLE;
		schedule_timeout(0);
	} else {	/* Interrupt driven */
		cli();
		enable_interrupts();
		interruptible_sleep_on(&cdu535_irq_wait);
		sti();
	}
}

/*------------------start of SONY CDU535 very specific ---------------------*/

/****************************************************************************
 * void select_unit( int unit_no )
 *
 *  Select the specified unit (0-3) so that subsequent commands reference it
 ****************************************************************************/
static void
select_unit(int unit_no)
{
	unsigned int select_mask = ~(1 << unit_no);
	outb(select_mask, select_unit_reg);
}

/***************************************************************************
 * int read_result_reg( Byte *data_ptr )
 *
 *  Read a result byte from the Sony CDU controller, store in location pointed
 * to by data_ptr.  Return zero on success, TIME_OUT if we did not receive
 * data.
 ***************************************************************************/
static int
read_result_reg(Byte *data_ptr)
{
	unsigned long snap;
	int read_status;

	snap = jiffies;
	while (jiffies-snap < SONY_JIFFIES_TIMEOUT) {
		read_status = inb(read_status_reg);
		if ((read_status & SONY535_RESULT_NOT_READY_BIT) == 0) {
#if DEBUG > 1
			printk(CDU535_MESSAGE_NAME
					": read_result_reg(): readStatReg = 0x%x\n", read_status);
#endif
			*data_ptr = inb(result_reg);
			return 0;
		} else {
			sony_sleep();
		}
	}
	printk(CDU535_MESSAGE_NAME " read_result_reg: TIME OUT!\n");
	return TIME_OUT;
}

/****************************************************************************
 * int read_exec_status( Byte status[2] )
 *
 *  Read the execution status of the last command and put into status.
 * Handles reading second status word if available.  Returns 0 on success,
 * TIME_OUT on failure.
 ****************************************************************************/
static int
read_exec_status(Byte status[2])
{
	status[1] = 0;
	if (read_result_reg(&(status[0])) != 0)
		return TIME_OUT;
	if ((status[0] & 0x80) != 0) {	/* byte two follows */
		if (read_result_reg(&(status[1])) != 0)
			return TIME_OUT;
	}
#if DEBUG > 1
	printk(CDU535_MESSAGE_NAME ": read_exec_status: read 0x%x 0x%x\n",
			status[0], status[1]);
#endif
	return 0;
}

/****************************************************************************
 * int check_drive_status( void )
 *
 *  Check the current drive status.  Using this before executing a command
 * takes care of the problem of unsolicited drive status-2 messages.
 * Add a check of the audio status if we think the disk is playing.
 ****************************************************************************/
static int

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -