📄 sonycd535.c
字号:
/*
* 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 + -