1853 lines
50 KiB
C
1853 lines
50 KiB
C
|
/*
|
||
|
* Sony CDU-31A CDROM interface device driver.
|
||
|
*
|
||
|
* Corey Minyard (minyard@wf-rch.cirr.com)
|
||
|
*
|
||
|
* 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 (unfortunatly) 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 ((retry_count > jiffies) && (! <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
|
||
|
* asyncronous events from the drive informing the driver that a disk
|
||
|
* has been inserted, removed, etc.
|
||
|
*
|
||
|
* 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/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 <asm/system.h>
|
||
|
#include <asm/io.h>
|
||
|
#include <asm/segment.h>
|
||
|
|
||
|
#include <linux/cdrom.h>
|
||
|
#include <linux/cdu31a.h>
|
||
|
|
||
|
#define MAJOR_NR CDU31A_CDROM_MAJOR
|
||
|
#include "blk.h"
|
||
|
|
||
|
#define CDU31A_MAX_CONSECUTIVE_ATTENTIONS 10
|
||
|
|
||
|
static unsigned short cdu31a_addresses[] =
|
||
|
{
|
||
|
0x340, /* Standard configuration Sony Interface */
|
||
|
0x1f88, /* Fusion CD-16 */
|
||
|
0x230, /* SoundBlaster 16 card */
|
||
|
0x360, /* Secondary standard Sony Interface */
|
||
|
0x320, /* Secondary standard Sony Interface */
|
||
|
0x330, /* Secondary standard Sony Interface */
|
||
|
0
|
||
|
};
|
||
|
|
||
|
|
||
|
static int handle_sony_cd_attention(void);
|
||
|
static int read_subcode(void);
|
||
|
static void sony_get_toc(void);
|
||
|
static int scd_open(struct inode *inode, struct file *filp);
|
||
|
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);
|
||
|
|
||
|
|
||
|
/* 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 short sony_cd_base_io = 0;
|
||
|
|
||
|
/*
|
||
|
* 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_disc_changed = 1; /* Has the disk been changed
|
||
|
since the last check? */
|
||
|
static int sony_toc_read = 0; /* Has the table of contents been
|
||
|
read? */
|
||
|
static int sony_spun_up = 0; /* Has the drive been spun up? */
|
||
|
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 = 0; /* How many processes have the
|
||
|
drive open. */
|
||
|
|
||
|
static volatile int sony_first_block = -1; /* First OS block (512 byte) in
|
||
|
the read-ahead buffer */
|
||
|
static volatile int sony_last_block = -1; /* Last OS block (512 byte) in
|
||
|
the read-ahead buffer */
|
||
|
|
||
|
static struct s_sony_toc *sony_toc; /* Points to the table of
|
||
|
contents. */
|
||
|
static struct s_sony_subcode * volatile last_sony_subcode; /* Points to the last
|
||
|
subcode address read */
|
||
|
static unsigned char * volatile sony_buffer; /* Points to the read-ahead
|
||
|
buffer */
|
||
|
|
||
|
static volatile int sony_inuse = 0; /* Is the drive in use? Only one operation at a time
|
||
|
allowed */
|
||
|
|
||
|
static struct wait_queue * sony_wait = NULL;
|
||
|
|
||
|
static struct task_struct *has_cd_task = NULL; /* The task that is currently using the
|
||
|
CDROM drive, or NULL if none. */
|
||
|
|
||
|
/*
|
||
|
* 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.
|
||
|
*/
|
||
|
unsigned volatile char cur_pos_msf[3] = { 0, 0, 0 };
|
||
|
unsigned volatile char final_pos_msf[3] = { 0, 0, 0 };
|
||
|
|
||
|
/*
|
||
|
* 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.
|
||
|
*/
|
||
|
int
|
||
|
check_cdu31a_media_change(int full_dev, int flag)
|
||
|
{
|
||
|
int retval, target;
|
||
|
|
||
|
|
||
|
target = MINOR(full_dev);
|
||
|
|
||
|
if (target > 0) {
|
||
|
printk("Sony CD-ROM request error: invalid device.\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
retval = sony_disc_changed;
|
||
|
if (!flag)
|
||
|
{
|
||
|
sony_disc_changed = 0;
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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)
|
||
|
{
|
||
|
current->state = TASK_INTERRUPTIBLE;
|
||
|
current->timeout = jiffies;
|
||
|
schedule();
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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 void
|
||
|
reset_drive(void)
|
||
|
{
|
||
|
outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg);
|
||
|
}
|
||
|
|
||
|
static inline void
|
||
|
clear_attention(void)
|
||
|
{
|
||
|
outb(SONY_ATTN_CLR_BIT, sony_cd_control_reg);
|
||
|
}
|
||
|
|
||
|
static inline void
|
||
|
clear_result_ready(void)
|
||
|
{
|
||
|
outb(SONY_RES_RDY_CLR_BIT, sony_cd_control_reg);
|
||
|
}
|
||
|
|
||
|
static inline void
|
||
|
clear_data_ready(void)
|
||
|
{
|
||
|
outb(SONY_DATA_RDY_CLR_BIT, sony_cd_control_reg);
|
||
|
}
|
||
|
|
||
|
static inline void
|
||
|
clear_param_reg(void)
|
||
|
{
|
||
|
outb(SONY_PARAM_CLR_BIT, sony_cd_control_reg);
|
||
|
}
|
||
|
|
||
|
static inline unsigned char
|
||
|
read_status_register(void)
|
||
|
{
|
||
|
return(inb(sony_cd_status_reg));
|
||
|
}
|
||
|
|
||
|
static inline unsigned char
|
||
|
read_result_register(void)
|
||
|
{
|
||
|
return(inb(sony_cd_result_reg));
|
||
|
}
|
||
|
|
||
|
static inline unsigned char
|
||
|
read_data_register(void)
|
||
|
{
|
||
|
return(inb(sony_cd_read_reg));
|
||
|
}
|
||
|
|
||
|
static inline void
|
||
|
write_param(unsigned char param)
|
||
|
{
|
||
|
outb(param, sony_cd_param_reg);
|
||
|
}
|
||
|
|
||
|
static inline void
|
||
|
write_cmd(unsigned char cmd)
|
||
|
{
|
||
|
outb(cmd, sony_cd_cmd_reg);
|
||
|
outb(SONY_RES_RDY_INT_EN_BIT, sony_cd_control_reg);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Set the drive parameters so the drive will auto-spin-up when a
|
||
|
* disk is inserted.
|
||
|
*/
|
||
|
static void
|
||
|
set_drive_params(void)
|
||
|
{
|
||
|
unsigned char res_reg[2];
|
||
|
unsigned int res_size;
|
||
|
unsigned char params[3];
|
||
|
|
||
|
|
||
|
params[0] = SONY_SD_MECH_CONTROL;
|
||
|
params[1] = 0x03;
|
||
|
do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
|
||
|
params,
|
||
|
2,
|
||
|
res_reg,
|
||
|
&res_size);
|
||
|
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
|
||
|
{
|
||
|
printk(" Unable to set mechanical parameters: 0x%2.2x\n", res_reg[1]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This code will reset the drive and attempt to restore sane parameters.
|
||
|
*/
|
||
|
static void
|
||
|
restart_on_error(void)
|
||
|
{
|
||
|
unsigned char res_reg[2];
|
||
|
unsigned int res_size;
|
||
|
unsigned int retry_count;
|
||
|
|
||
|
|
||
|
printk("cdu31a: Resetting drive on error\n");
|
||
|
reset_drive();
|
||
|
retry_count = jiffies + SONY_RESET_TIMEOUT;
|
||
|
while ((retry_count > jiffies) && (!is_attention()))
|
||
|
{
|
||
|
sony_sleep();
|
||
|
}
|
||
|
set_drive_params();
|
||
|
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
|
||
|
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
|
||
|
{
|
||
|
printk("cdu31a: Unable to spin up drive: 0x%2.2x\n", res_reg[1]);
|
||
|
}
|
||
|
|
||
|
current->state = TASK_INTERRUPTIBLE;
|
||
|
current->timeout = jiffies + 200;
|
||
|
schedule();
|
||
|
|
||
|
do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
|
||
|
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
|
||
|
{
|
||
|
printk("cdu31a: Unable to read TOC: 0x%2.2x\n", res_reg[1]);
|
||
|
}
|
||
|
sony_get_toc();
|
||
|
if (!sony_toc_read)
|
||
|
{
|
||
|
printk("cdu31a: Unable to get TOC data\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This routine writes data to the parameter register. Since this should
|
||
|
* happen fairly fast, it is polled with no OS waits between.
|
||
|
*/
|
||
|
static int
|
||
|
write_params(unsigned char *params,
|
||
|
int num_params)
|
||
|
{
|
||
|
unsigned int retry_count;
|
||
|
|
||
|
|
||
|
retry_count = SONY_READY_RETRIES;
|
||
|
while ((retry_count > 0) && (!is_param_write_rdy()))
|
||
|
{
|
||
|
retry_count--;
|
||
|
}
|
||
|
if (!is_param_write_rdy())
|
||
|
{
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
while (num_params > 0)
|
||
|
{
|
||
|
write_param(*params);
|
||
|
params++;
|
||
|
num_params--;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* The following reads data from the command result register. It is a
|
||
|
* fairly complex routine, all status info flows back through this
|
||
|
* interface. The algorithm is stolen directly from the flowcharts in
|
||
|
* the drive manual.
|
||
|
*/
|
||
|
static void
|
||
|
get_result(unsigned char *result_buffer,
|
||
|
unsigned int *result_size)
|
||
|
{
|
||
|
unsigned char a, b;
|
||
|
int i;
|
||
|
unsigned int retry_count;
|
||
|
|
||
|
|
||
|
while (handle_sony_cd_attention())
|
||
|
;
|
||
|
/* Wait for the result data to be ready */
|
||
|
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
|
||
|
while ((retry_count > jiffies) && (is_busy() || (!(is_result_ready()))))
|
||
|
{
|
||
|
sony_sleep();
|
||
|
|
||
|
while (handle_sony_cd_attention())
|
||
|
;
|
||
|
}
|
||
|
if (is_busy() || (!(is_result_ready())))
|
||
|
{
|
||
|
result_buffer[0] = 0x20;
|
||
|
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
|
||
|
*result_size = 2;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get the first two bytes. This determines what else needs
|
||
|
* to be done.
|
||
|
*/
|
||
|
clear_result_ready();
|
||
|
a = read_result_register();
|
||
|
*result_buffer = a;
|
||
|
result_buffer++;
|
||
|
b = read_result_register();
|
||
|
*result_buffer = b;
|
||
|
result_buffer++;
|
||
|
*result_size = 2;
|
||
|
|
||
|
/*
|
||
|
* 0x20 means an error occured. Byte 2 will have the error code.
|
||
|
* Otherwise, the command succeded, byte 2 will have the count of
|
||
|
* how many more status bytes are coming.
|
||
|
*
|
||
|
* The result register can be read 10 bytes at a time, a wait for
|
||
|
* result ready to be asserted must be done between every 10 bytes.
|
||
|
*/
|
||
|
if ((a & 0xf0) != 0x20)
|
||
|
{
|
||
|
if (b > 8)
|
||
|
{
|
||
|
for (i=0; i<8; i++)
|
||
|
{
|
||
|
*result_buffer = read_result_register();
|
||
|
result_buffer++;
|
||
|
(*result_size)++;
|
||
|
}
|
||
|
b = b - 8;
|
||
|
|
||
|
while (b > 10)
|
||
|
{
|
||
|
retry_count = SONY_READY_RETRIES;
|
||
|
while ((retry_count > 0) && (!is_result_ready()))
|
||
|
{
|
||
|
retry_count--;
|
||
|
}
|
||
|
if (!is_result_ready())
|
||
|
{
|
||
|
result_buffer[0] = 0x20;
|
||
|
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
|
||
|
*result_size = 2;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
clear_result_ready();
|
||
|
|
||
|
for (i=0; i<10; i++)
|
||
|
{
|
||
|
*result_buffer = read_result_register();
|
||
|
result_buffer++;
|
||
|
(*result_size)++;
|
||
|
}
|
||
|
b = b - 10;
|
||
|
}
|
||
|
|
||
|
if (b > 0)
|
||
|
{
|
||
|
retry_count = SONY_READY_RETRIES;
|
||
|
while ((retry_count > 0) && (!is_result_ready()))
|
||
|
{
|
||
|
retry_count--;
|
||
|
}
|
||
|
if (!is_result_ready())
|
||
|
{
|
||
|
result_buffer[0] = 0x20;
|
||
|
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
|
||
|
*result_size = 2;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while (b > 0)
|
||
|
{
|
||
|
*result_buffer = read_result_register();
|
||
|
result_buffer++;
|
||
|
(*result_size)++;
|
||
|
b--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Read in a 2048 byte block of data.
|
||
|
*/
|
||
|
static void
|
||
|
read_data_block(unsigned char *data,
|
||
|
unsigned char *result_buffer,
|
||
|
unsigned int *result_size)
|
||
|
{
|
||
|
int i;
|
||
|
unsigned int retry_count;
|
||
|
|
||
|
for (i=0; i<2048; i++)
|
||
|
{
|
||
|
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
|
||
|
while ((retry_count > jiffies) && (!is_data_requested()))
|
||
|
{
|
||
|
while (handle_sony_cd_attention())
|
||
|
;
|
||
|
|
||
|
sony_sleep();
|
||
|
}
|
||
|
if (!is_data_requested())
|
||
|
{
|
||
|
result_buffer[0] = 0x20;
|
||
|
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
|
||
|
*result_size = 2;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
*data = read_data_register();
|
||
|
data++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This routine issues a read data command and gets the data. I don't
|
||
|
* really like the way this is done (I would prefer for do_sony_cmd() to
|
||
|
* handle it automatically) but I found that the drive returns status
|
||
|
* when it finishes reading (not when the host has read all the data)
|
||
|
* or after it gets an error. This means that the status can be
|
||
|
* received at any time and should be handled immediately (at least
|
||
|
* between every 2048 byte block) to check for errors, we can't wait
|
||
|
* until all the data is read.
|
||
|
*
|
||
|
* This routine returns the total number of sectors read. It will
|
||
|
* not return an error if it reads at least one sector successfully.
|
||
|
*/
|
||
|
static unsigned int
|
||
|
get_data(unsigned char *orig_data,
|
||
|
unsigned char *params, /* 6 bytes with the MSF start address
|
||
|
and number of sectors to read. */
|
||
|
unsigned int orig_data_size,
|
||
|
unsigned char *result_buffer,
|
||
|
unsigned int *result_size)
|
||
|
{
|
||
|
unsigned int cur_offset;
|
||
|
unsigned int retry_count;
|
||
|
int result_read;
|
||
|
int num_retries;
|
||
|
unsigned int num_sectors_read = 0;
|
||
|
unsigned char *data = orig_data;
|
||
|
unsigned int data_size = orig_data_size;
|
||
|
|
||
|
|
||
|
cli();
|
||
|
while (sony_inuse)
|
||
|
{
|
||
|
interruptible_sleep_on(&sony_wait);
|
||
|
if (current->signal & ~current->blocked)
|
||
|
{
|
||
|
result_buffer[0] = 0x20;
|
||
|
result_buffer[1] = SONY_SIGNAL_OP_ERR;
|
||
|
*result_size = 2;
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
sony_inuse = 1;
|
||
|
has_cd_task = current;
|
||
|
sti();
|
||
|
|
||
|
num_retries = 0;
|
||
|
retry_data_operation:
|
||
|
result_buffer[0] = 0;
|
||
|
result_buffer[1] = 0;
|
||
|
|
||
|
/*
|
||
|
* Clear any outstanding attentions and wait for the drive to
|
||
|
* complete any pending operations.
|
||
|
*/
|
||
|
while (handle_sony_cd_attention())
|
||
|
;
|
||
|
|
||
|
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
|
||
|
while ((retry_count > jiffies) && (is_busy()))
|
||
|
{
|
||
|
sony_sleep();
|
||
|
|
||
|
while (handle_sony_cd_attention())
|
||
|
;
|
||
|
}
|
||
|
|
||
|
if (is_busy())
|
||
|
{
|
||
|
result_buffer[0] = 0x20;
|
||
|
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
|
||
|
*result_size = 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Issue the command */
|
||
|
clear_result_ready();
|
||
|
clear_param_reg();
|
||
|
|
||
|
write_params(params, 6);
|
||
|
write_cmd(SONY_READ_CMD);
|
||
|
|
||
|
/*
|
||
|
* Read the data from the drive one 2048 byte sector at a time. Handle
|
||
|
* any results received between sectors, if an error result is returned
|
||
|
* terminate the operation immediately.
|
||
|
*/
|
||
|
cur_offset = 0;
|
||
|
result_read = 0;
|
||
|
while ((data_size > 0) && (result_buffer[0] == 0))
|
||
|
{
|
||
|
/* Wait for the drive to tell us we have something */
|
||
|
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
|
||
|
while ((retry_count > jiffies) && (!(is_result_ready() || is_data_ready())))
|
||
|
{
|
||
|
while (handle_sony_cd_attention())
|
||
|
;
|
||
|
|
||
|
sony_sleep();
|
||
|
}
|
||
|
if (!(is_result_ready() || is_data_ready()))
|
||
|
{
|
||
|
result_buffer[0] = 0x20;
|
||
|
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
|
||
|
*result_size = 2;
|
||
|
}
|
||
|
|
||
|
/* Handle results first */
|
||
|
else if (is_result_ready())
|
||
|
{
|
||
|
result_read = 1;
|
||
|
get_result(result_buffer, result_size);
|
||
|
}
|
||
|
else /* Handle data next */
|
||
|
{
|
||
|
/*
|
||
|
* The drive has to be polled for status on a byte-by-byte basis
|
||
|
* to know if the data is ready. Yuck. I really wish I could use DMA.
|
||
|
*/
|
||
|
clear_data_ready();
|
||
|
read_data_block(data, result_buffer, result_size);
|
||
|
data += 2048;
|
||
|
data_size -= 2048;
|
||
|
cur_offset = cur_offset + 2048;
|
||
|
num_sectors_read++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Make sure the result has been read */
|
||
|
if (!result_read)
|
||
|
{
|
||
|
get_result(result_buffer, result_size);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ((result_buffer[0] & 0x20) == 0x20)
|
||
|
&& (result_buffer[1] != SONY_NOT_SPIN_ERR) /* No retry when not spin */
|
||
|
&& (num_retries < MAX_CDU31A_RETRIES))
|
||
|
{
|
||
|
/*
|
||
|
* If an error occurs, go back and only read one sector at the
|
||
|
* given location. Hopefully the error occurred on an unused
|
||
|
* sector after the first one. It is hard to say which sector
|
||
|
* the error occurred on because the drive returns status before
|
||
|
* the data transfer is finished and doesn't say which sector.
|
||
|
*/
|
||
|
data_size = 2048;
|
||
|
data = orig_data;
|
||
|
num_sectors_read = 0;
|
||
|
size_to_buf(1, ¶ms[3]);
|
||
|
|
||
|
num_retries++;
|
||
|
/* Issue a reset on an error (the second time), othersize just delay */
|
||
|
if (num_retries == 2)
|
||
|
{
|
||
|
restart_on_error();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
current->state = TASK_INTERRUPTIBLE;
|
||
|
current->timeout = jiffies + 10;
|
||
|
schedule();
|
||
|
}
|
||
|
|
||
|
/* Restart the operation. */
|
||
|
goto retry_data_operation;
|
||
|
}
|
||
|
|
||
|
has_cd_task = NULL;
|
||
|
sony_inuse = 0;
|
||
|
wake_up_interruptible(&sony_wait);
|
||
|
|
||
|
return(num_sectors_read);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Do a command that does not involve data transfer. This routine must
|
||
|
* be re-entrant from the same task to support being called from the
|
||
|
* data operation code when an error occurs.
|
||
|
*/
|
||
|
static void
|
||
|
do_sony_cd_cmd(unsigned char cmd,
|
||
|
unsigned char *params,
|
||
|
unsigned int num_params,
|
||
|
unsigned char *result_buffer,
|
||
|
unsigned int *result_size)
|
||
|
{
|
||
|
unsigned int retry_count;
|
||
|
int num_retries;
|
||
|
int recursive_call;
|
||
|
|
||
|
|
||
|
cli();
|
||
|
if (current != has_cd_task) /* Allow recursive calls to this routine */
|
||
|
{
|
||
|
while (sony_inuse)
|
||
|
{
|
||
|
interruptible_sleep_on(&sony_wait);
|
||
|
if (current->signal & ~current->blocked)
|
||
|
{
|
||
|
result_buffer[0] = 0x20;
|
||
|
result_buffer[1] = SONY_SIGNAL_OP_ERR;
|
||
|
*result_size = 2;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
sony_inuse = 1;
|
||
|
has_cd_task = current;
|
||
|
recursive_call = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
recursive_call = 1;
|
||
|
}
|
||
|
sti();
|
||
|
|
||
|
num_retries = 0;
|
||
|
retry_cd_operation:
|
||
|
|
||
|
while (handle_sony_cd_attention())
|
||
|
;
|
||
|
|
||
|
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
|
||
|
while ((retry_count > jiffies) && (is_busy()))
|
||
|
{
|
||
|
sony_sleep();
|
||
|
|
||
|
while (handle_sony_cd_attention())
|
||
|
;
|
||
|
}
|
||
|
if (is_busy())
|
||
|
{
|
||
|
result_buffer[0] = 0x20;
|
||
|
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
|
||
|
*result_size = 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
clear_result_ready();
|
||
|
clear_param_reg();
|
||
|
|
||
|
write_params(params, num_params);
|
||
|
write_cmd(cmd);
|
||
|
|
||
|
get_result(result_buffer, result_size);
|
||
|
}
|
||
|
|
||
|
if ( ((result_buffer[0] & 0x20) == 0x20)
|
||
|
&& (num_retries < MAX_CDU31A_RETRIES))
|
||
|
{
|
||
|
num_retries++;
|
||
|
current->state = TASK_INTERRUPTIBLE;
|
||
|
current->timeout = jiffies + 10; /* Wait .1 seconds on retries */
|
||
|
schedule();
|
||
|
goto retry_cd_operation;
|
||
|
}
|
||
|
|
||
|
if (!recursive_call)
|
||
|
{
|
||
|
has_cd_task = NULL;
|
||
|
sony_inuse = 0;
|
||
|
wake_up_interruptible(&sony_wait);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Handle an attention from the drive. This will return 1 if it found one
|
||
|
* or 0 if not (if one is found, the caller might want to call again).
|
||
|
*
|
||
|
* This routine counts the number of consecutive times it is called
|
||
|
* (since this is always called from a while loop until it returns
|
||
|
* a 0), and returns a 0 if it happens too many times. This will help
|
||
|
* prevent a lockup.
|
||
|
*/
|
||
|
static int
|
||
|
handle_sony_cd_attention(void)
|
||
|
{
|
||
|
unsigned char atten_code;
|
||
|
static int num_consecutive_attentions = 0;
|
||
|
|
||
|
|
||
|
if (is_attention())
|
||
|
{
|
||
|
if (num_consecutive_attentions > CDU31A_MAX_CONSECUTIVE_ATTENTIONS)
|
||
|
{
|
||
|
printk("cdu31a: Too many consecutive attentions: %d\n",
|
||
|
num_consecutive_attentions);
|
||
|
num_consecutive_attentions = 0;
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
clear_attention();
|
||
|
atten_code = read_result_register();
|
||
|
|
||
|
switch (atten_code)
|
||
|
{
|
||
|
/* Someone changed the CD. Mark it as changed */
|
||
|
case SONY_MECH_LOADED_ATTN:
|
||
|
sony_disc_changed = 1;
|
||
|
sony_toc_read = 0;
|
||
|
sony_audio_status = CDROM_AUDIO_NO_STATUS;
|
||
|
sony_first_block = -1;
|
||
|
sony_last_block = -1;
|
||
|
break;
|
||
|
|
||
|
case SONY_AUDIO_PLAY_DONE_ATTN:
|
||
|
sony_audio_status = CDROM_AUDIO_COMPLETED;
|
||
|
read_subcode();
|
||
|
break;
|
||
|
|
||
|
case SONY_EJECT_PUSHED_ATTN:
|
||
|
sony_audio_status = CDROM_AUDIO_INVALID;
|
||
|
break;
|
||
|
|
||
|
case SONY_LEAD_IN_ERR_ATTN:
|
||
|
case SONY_LEAD_OUT_ERR_ATTN:
|
||
|
case SONY_DATA_TRACK_ERR_ATTN:
|
||
|
case SONY_AUDIO_PLAYBACK_ERR_ATTN:
|
||
|
sony_audio_status = CDROM_AUDIO_ERROR;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
num_consecutive_attentions++;
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
num_consecutive_attentions = 0;
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Convert from an integer 0-99 to BCD */
|
||
|
static inline unsigned int
|
||
|
int_to_bcd(unsigned int val)
|
||
|
{
|
||
|
int retval;
|
||
|
|
||
|
|
||
|
retval = (val / 10) << 4;
|
||
|
retval = retval | val % 10;
|
||
|
return(retval);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Convert from BCD to an integer from 0-99 */
|
||
|
static unsigned int
|
||
|
bcd_to_int(unsigned int bcd)
|
||
|
{
|
||
|
return((((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f));
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Convert a logical sector value (like the OS would want to use for
|
||
|
* a block device) to an MSF format.
|
||
|
*/
|
||
|
static void
|
||
|
log_to_msf(unsigned int log, unsigned char *msf)
|
||
|
{
|
||
|
log = log + LOG_START_OFFSET;
|
||
|
msf[0] = int_to_bcd(log / 4500);
|
||
|
log = log % 4500;
|
||
|
msf[1] = int_to_bcd(log / 75);
|
||
|
msf[2] = int_to_bcd(log % 75);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Convert an MSF format to a logical sector.
|
||
|
*/
|
||
|
static unsigned int
|
||
|
msf_to_log(unsigned char *msf)
|
||
|
{
|
||
|
unsigned int log;
|
||
|
|
||
|
|
||
|
log = bcd_to_int(msf[2]);
|
||
|
log += bcd_to_int(msf[1]) * 75;
|
||
|
log += bcd_to_int(msf[0]) * 4500;
|
||
|
log = log - LOG_START_OFFSET;
|
||
|
|
||
|
return log;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Take in integer size value and put it into a buffer like
|
||
|
* the drive would want to see a number-of-sector value.
|
||
|
*/
|
||
|
static void
|
||
|
size_to_buf(unsigned int size,
|
||
|
unsigned char *buf)
|
||
|
{
|
||
|
buf[0] = size / 65536;
|
||
|
size = size % 65536;
|
||
|
buf[1] = size / 256;
|
||
|
buf[2] = size % 256;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* The OS calls this to perform a read or write operation to the drive.
|
||
|
* Write obviously fail. Reads to a read ahead of sony_buffer_size
|
||
|
* bytes to help speed operations. This especially helps since the OS
|
||
|
* uses 1024 byte blocks and the drive uses 2048 byte blocks. Since most
|
||
|
* data access on a CD is done sequentially, this saves a lot of operations.
|
||
|
*/
|
||
|
static void
|
||
|
do_cdu31a_request(void)
|
||
|
{
|
||
|
int block;
|
||
|
unsigned int dev;
|
||
|
int nsect;
|
||
|
unsigned char params[10];
|
||
|
unsigned char res_reg[2];
|
||
|
unsigned int res_size;
|
||
|
int copyoff;
|
||
|
int spin_up_retry;
|
||
|
unsigned int read_size;
|
||
|
|
||
|
|
||
|
if (!sony_spun_up)
|
||
|
{
|
||
|
scd_open (NULL,NULL);
|
||
|
}
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
cdu31a_request_startover:
|
||
|
/*
|
||
|
* The beginning here is stolen from the hard disk driver. I hope
|
||
|
* its right.
|
||
|
*/
|
||
|
if (!(CURRENT) || CURRENT->dev < 0)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
INIT_REQUEST;
|
||
|
dev = MINOR(CURRENT->dev);
|
||
|
block = CURRENT->sector;
|
||
|
nsect = CURRENT->nr_sectors;
|
||
|
if (dev != 0)
|
||
|
{
|
||
|
end_request(0);
|
||
|
goto cdu31a_request_startover;
|
||
|
}
|
||
|
|
||
|
switch(CURRENT->cmd)
|
||
|
{
|
||
|
case READ:
|
||
|
/*
|
||
|
* If the block address is invalid or the request goes beyond the end of
|
||
|
* the media, return an error.
|
||
|
*/
|
||
|
if ((block / 4) >= sony_toc->lead_out_start_lba)
|
||
|
{
|
||
|
end_request(0);
|
||
|
goto cdu31a_request_startover;
|
||
|
}
|
||
|
if (((block + nsect) / 4) >= sony_toc->lead_out_start_lba)
|
||
|
{
|
||
|
end_request(0);
|
||
|
goto cdu31a_request_startover;
|
||
|
}
|
||
|
|
||
|
while (nsect > 0)
|
||
|
{
|
||
|
/*
|
||
|
* If the requested sector is not currently in the read-ahead buffer,
|
||
|
* it must be read in.
|
||
|
*/
|
||
|
if ((block < sony_first_block) || (block > sony_last_block))
|
||
|
{
|
||
|
sony_first_block = (block / 4) * 4;
|
||
|
log_to_msf(block/4, params);
|
||
|
|
||
|
/*
|
||
|
* If the full read-ahead would go beyond the end of the media, trim
|
||
|
* it back to read just till the end of the media.
|
||
|
*/
|
||
|
if (((block / 4) + sony_buffer_sectors) >= sony_toc->lead_out_start_lba)
|
||
|
{
|
||
|
read_size = sony_toc->lead_out_start_lba - (block / 4);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
read_size = sony_buffer_sectors;
|
||
|
}
|
||
|
size_to_buf(read_size, ¶ms[3]);
|
||
|
|
||
|
/*
|
||
|
* Read the data. If the drive was not spinning, spin it up and try
|
||
|
* once more. I know, the goto is ugly, but I am too lazy to fix it.
|
||
|
*/
|
||
|
spin_up_retry = 0;
|
||
|
try_read_again:
|
||
|
sony_last_block = sony_first_block
|
||
|
+ (get_data(sony_buffer,
|
||
|
params,
|
||
|
(read_size * 2048),
|
||
|
res_reg,
|
||
|
&res_size) * 4) - 1;
|
||
|
if ((res_size < 2) || (res_reg[0] != 0))
|
||
|
{
|
||
|
if ((res_reg[1] == SONY_NOT_SPIN_ERR) && (!spin_up_retry))
|
||
|
{
|
||
|
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
|
||
|
spin_up_retry = 1;
|
||
|
goto try_read_again;
|
||
|
}
|
||
|
|
||
|
printk("Sony CDROM Read error: 0x%2.2x\n", res_reg[1]);
|
||
|
sony_first_block = -1;
|
||
|
sony_last_block = -1;
|
||
|
end_request(0);
|
||
|
goto cdu31a_request_startover;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* The data is in memory now, copy it to the buffer and advance to the
|
||
|
* next block to read.
|
||
|
*/
|
||
|
copyoff = (block - sony_first_block) * 512;
|
||
|
memcpy(CURRENT->buffer, sony_buffer+copyoff, 512);
|
||
|
|
||
|
block += 1;
|
||
|
nsect -= 1;
|
||
|
CURRENT->buffer += 512;
|
||
|
}
|
||
|
|
||
|
end_request(1);
|
||
|
break;
|
||
|
|
||
|
case WRITE:
|
||
|
end_request(0);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
panic("Unkown SONY CD cmd");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Read the table of contents from the drive and set sony_toc_read if
|
||
|
* successful.
|
||
|
*/
|
||
|
static void
|
||
|
sony_get_toc(void)
|
||
|
{
|
||
|
unsigned int res_size;
|
||
|
|
||
|
|
||
|
if (!sony_toc_read)
|
||
|
{
|
||
|
do_sony_cd_cmd(SONY_REQ_TOC_DATA_CMD,
|
||
|
NULL,
|
||
|
0,
|
||
|
(unsigned char *) sony_toc,
|
||
|
&res_size);
|
||
|
if ((res_size < 2) || ((sony_toc->exec_status[0] & 0x20) == 0x20))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf);
|
||
|
sony_toc_read = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Search for a specific track in the table of contents.
|
||
|
*/
|
||
|
static int
|
||
|
find_track(int track)
|
||
|
{
|
||
|
int i;
|
||
|
int num_tracks;
|
||
|
|
||
|
|
||
|
num_tracks = sony_toc->last_track_num + sony_toc->first_track_num + 1;
|
||
|
for (i = 0; i < num_tracks; i++)
|
||
|
{
|
||
|
if (sony_toc->tracks[i].track == track)
|
||
|
{
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Read the subcode and put it int last_sony_subcode for future use.
|
||
|
*/
|
||
|
static int
|
||
|
read_subcode(void)
|
||
|
{
|
||
|
unsigned int res_size;
|
||
|
|
||
|
|
||
|
do_sony_cd_cmd(SONY_REQ_SUBCODE_ADDRESS_CMD,
|
||
|
NULL,
|
||
|
0,
|
||
|
(unsigned char *) last_sony_subcode,
|
||
|
&res_size);
|
||
|
if ((res_size < 2) || ((last_sony_subcode->exec_status[0] & 0x20) == 0x20))
|
||
|
{
|
||
|
printk("Sony CDROM error 0x%2.2x (read_subcode)\n",
|
||
|
last_sony_subcode->exec_status[1]);
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Get the subchannel info like the CDROMSUBCHNL command wants to see it. If
|
||
|
* the drive is playing, the subchannel needs to be read (since it would be
|
||
|
* changing). If the drive is paused or completed, the subcode information has
|
||
|
* already been stored, just use that. The ioctl call wants things in decimal
|
||
|
* (not BCD), so all the conversions are done.
|
||
|
*/
|
||
|
static int
|
||
|
sony_get_subchnl_info(long arg)
|
||
|
{
|
||
|
struct cdrom_subchnl schi;
|
||
|
|
||
|
|
||
|
/* Get attention stuff */
|
||
|
while (handle_sony_cd_attention())
|
||
|
;
|
||
|
|
||
|
sony_get_toc();
|
||
|
if (!sony_toc_read)
|
||
|
{
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
verify_area(VERIFY_READ, (char *) arg, sizeof(schi));
|
||
|
verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));
|
||
|
|
||
|
memcpy_fromfs(&schi, (char *) arg, sizeof(schi));
|
||
|
|
||
|
switch (sony_audio_status)
|
||
|
{
|
||
|
case CDROM_AUDIO_PLAY:
|
||
|
if (read_subcode() < 0)
|
||
|
{
|
||
|
return -EIO;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case CDROM_AUDIO_PAUSED:
|
||
|
case CDROM_AUDIO_COMPLETED:
|
||
|
break;
|
||
|
|
||
|
case CDROM_AUDIO_NO_STATUS:
|
||
|
schi.cdsc_audiostatus = sony_audio_status;
|
||
|
memcpy_tofs((char *) arg, &schi, sizeof(schi));
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
case CDROM_AUDIO_INVALID:
|
||
|
case CDROM_AUDIO_ERROR:
|
||
|
default:
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
schi.cdsc_audiostatus = sony_audio_status;
|
||
|
schi.cdsc_adr = last_sony_subcode->address;
|
||
|
schi.cdsc_ctrl = last_sony_subcode->control;
|
||
|
schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num);
|
||
|
schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num);
|
||
|
if (schi.cdsc_format == CDROM_MSF)
|
||
|
{
|
||
|
schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]);
|
||
|
schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]);
|
||
|
schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]);
|
||
|
|
||
|
schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]);
|
||
|
schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]);
|
||
|
schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]);
|
||
|
}
|
||
|
else if (schi.cdsc_format == CDROM_LBA)
|
||
|
{
|
||
|
schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf);
|
||
|
schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf);
|
||
|
}
|
||
|
|
||
|
memcpy_tofs((char *) arg, &schi, sizeof(schi));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* The big ugly ioctl handler.
|
||
|
*/
|
||
|
static int
|
||
|
scd_ioctl(struct inode *inode,
|
||
|
struct file *file,
|
||
|
unsigned int cmd,
|
||
|
unsigned long arg)
|
||
|
{
|
||
|
unsigned int dev;
|
||
|
unsigned char res_reg[2];
|
||
|
unsigned int res_size;
|
||
|
unsigned char params[7];
|
||
|
int i;
|
||
|
|
||
|
|
||
|
if (!inode)
|
||
|
{
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
dev = MINOR(inode->i_rdev) >> 6;
|
||
|
if (dev != 0)
|
||
|
{
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
switch (cmd)
|
||
|
{
|
||
|
case CDROMSTART: /* Spin up the drive */
|
||
|
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
|
||
|
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
|
||
|
{
|
||
|
printk("Sony CDROM error 0x%2.2x (CDROMSTART)\n", res_reg[1]);
|
||
|
return -EIO;
|
||
|
}
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
case CDROMSTOP: /* Spin down the drive */
|
||
|
do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
|
||
|
|
||
|
/*
|
||
|
* Spin the drive down, ignoring the error if the disk was
|
||
|
* already not spinning.
|
||
|
*/
|
||
|
sony_audio_status = CDROM_AUDIO_NO_STATUS;
|
||
|
do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
|
||
|
if ( ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
|
||
|
&& (res_reg[1] != SONY_NOT_SPIN_ERR))
|
||
|
{
|
||
|
printk("Sony CDROM error 0x%2.2x (CDROMSTOP)\n", res_reg[1]);
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
case CDROMPAUSE: /* Pause the drive */
|
||
|
do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
|
||
|
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
|
||
|
{
|
||
|
printk("Sony CDROM error 0x%2.2x (CDROMPAUSE)\n", res_reg[1]);
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
/* Get the current position and save it for resuming */
|
||
|
if (read_subcode() < 0)
|
||
|
{
|
||
|
return -EIO;
|
||
|
}
|
||
|
cur_pos_msf[0] = last_sony_subcode->abs_msf[0];
|
||
|
cur_pos_msf[1] = last_sony_subcode->abs_msf[1];
|
||
|
cur_pos_msf[2] = last_sony_subcode->abs_msf[2];
|
||
|
sony_audio_status = CDROM_AUDIO_PAUSED;
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
case CDROMRESUME: /* Start the drive after being paused */
|
||
|
if (sony_audio_status != CDROM_AUDIO_PAUSED)
|
||
|
{
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
|
||
|
|
||
|
/* Start the drive at the saved position. */
|
||
|
params[1] = cur_pos_msf[0];
|
||
|
params[2] = cur_pos_msf[1];
|
||
|
params[3] = cur_pos_msf[2];
|
||
|
params[4] = final_pos_msf[0];
|
||
|
params[5] = final_pos_msf[1];
|
||
|
params[6] = final_pos_msf[2];
|
||
|
params[0] = 0x03;
|
||
|
do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
|
||
|
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
|
||
|
{
|
||
|
printk("Sony CDROM error 0x%2.2x (CDROMRESUME)\n", res_reg[1]);
|
||
|
return -EIO;
|
||
|
}
|
||
|
sony_audio_status = CDROM_AUDIO_PLAY;
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
case CDROMPLAYMSF: /* Play starting at the given MSF address. */
|
||
|
verify_area(VERIFY_READ, (char *) arg, 6);
|
||
|
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
|
||
|
memcpy_fromfs(&(params[1]), (void *) arg, 6);
|
||
|
|
||
|
/* The parameters are given in int, must be converted */
|
||
|
for (i=1; i<7; i++)
|
||
|
{
|
||
|
params[i] = int_to_bcd(params[i]);
|
||
|
}
|
||
|
params[0] = 0x03;
|
||
|
do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
|
||
|
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
|
||
|
{
|
||
|
printk("Sony CDROM error 0x%2.2x (CDROMPLAYMSF)\n", res_reg[1]);
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
/* Save the final position for pauses and resumes */
|
||
|
final_pos_msf[0] = params[4];
|
||
|
final_pos_msf[1] = params[5];
|
||
|
final_pos_msf[2] = params[6];
|
||
|
sony_audio_status = CDROM_AUDIO_PLAY;
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
case CDROMREADTOCHDR: /* Read the table of contents header */
|
||
|
{
|
||
|
struct cdrom_tochdr *hdr;
|
||
|
struct cdrom_tochdr loc_hdr;
|
||
|
|
||
|
sony_get_toc();
|
||
|
if (!sony_toc_read)
|
||
|
{
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
hdr = (struct cdrom_tochdr *) arg;
|
||
|
verify_area(VERIFY_WRITE, hdr, sizeof(*hdr));
|
||
|
loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num);
|
||
|
loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num);
|
||
|
memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr));
|
||
|
}
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
case CDROMREADTOCENTRY: /* Read a given table of contents entry */
|
||
|
{
|
||
|
struct cdrom_tocentry *entry;
|
||
|
struct cdrom_tocentry loc_entry;
|
||
|
int track_idx;
|
||
|
unsigned char *msf_val = NULL;
|
||
|
|
||
|
sony_get_toc();
|
||
|
if (!sony_toc_read)
|
||
|
{
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
entry = (struct cdrom_tocentry *) arg;
|
||
|
verify_area(VERIFY_READ, entry, sizeof(*entry));
|
||
|
verify_area(VERIFY_WRITE, entry, sizeof(*entry));
|
||
|
|
||
|
memcpy_fromfs(&loc_entry, entry, sizeof(loc_entry));
|
||
|
|
||
|
/* Lead out is handled separately since it is special. */
|
||
|
if (loc_entry.cdte_track == CDROM_LEADOUT)
|
||
|
{
|
||
|
loc_entry.cdte_adr = sony_toc->address2;
|
||
|
loc_entry.cdte_ctrl = sony_toc->control2;
|
||
|
msf_val = sony_toc->lead_out_start_msf;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
|
||
|
if (track_idx < 0)
|
||
|
{
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
loc_entry.cdte_adr = sony_toc->tracks[track_idx].address;
|
||
|
loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control;
|
||
|
msf_val = sony_toc->tracks[track_idx].track_start_msf;
|
||
|
}
|
||
|
|
||
|
/* Logical buffer address or MSF format requested? */
|
||
|
if (loc_entry.cdte_format == CDROM_LBA)
|
||
|
{
|
||
|
loc_entry.cdte_addr.lba = msf_to_log(msf_val);
|
||
|
}
|
||
|
else if (loc_entry.cdte_format == CDROM_MSF)
|
||
|
{
|
||
|
loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val);
|
||
|
loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val+1));
|
||
|
loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val+2));
|
||
|
}
|
||
|
memcpy_tofs(entry, &loc_entry, sizeof(*entry));
|
||
|
}
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */
|
||
|
{
|
||
|
struct cdrom_ti ti;
|
||
|
int track_idx;
|
||
|
|
||
|
sony_get_toc();
|
||
|
if (!sony_toc_read)
|
||
|
{
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
verify_area(VERIFY_READ, (char *) arg, sizeof(ti));
|
||
|
|
||
|
memcpy_fromfs(&ti, (char *) arg, sizeof(ti));
|
||
|
if ( (ti.cdti_trk0 < sony_toc->first_track_num)
|
||
|
|| (ti.cdti_trk0 > sony_toc->last_track_num)
|
||
|
|| (ti.cdti_trk1 < ti.cdti_trk0))
|
||
|
{
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
track_idx = find_track(int_to_bcd(ti.cdti_trk0));
|
||
|
if (track_idx < 0)
|
||
|
{
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
params[1] = sony_toc->tracks[track_idx].track_start_msf[0];
|
||
|
params[2] = sony_toc->tracks[track_idx].track_start_msf[1];
|
||
|
params[3] = sony_toc->tracks[track_idx].track_start_msf[2];
|
||
|
|
||
|
/*
|
||
|
* If we want to stop after the last track, use the lead-out
|
||
|
* MSF to do that.
|
||
|
*/
|
||
|
if (ti.cdti_trk1 >= bcd_to_int(sony_toc->last_track_num))
|
||
|
{
|
||
|
log_to_msf(msf_to_log(sony_toc->lead_out_start_msf)-1,
|
||
|
&(params[4]));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
track_idx = find_track(int_to_bcd(ti.cdti_trk1+1));
|
||
|
if (track_idx < 0)
|
||
|
{
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf)-1,
|
||
|
&(params[4]));
|
||
|
}
|
||
|
params[0] = 0x03;
|
||
|
|
||
|
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
|
||
|
|
||
|
do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
|
||
|
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
|
||
|
{
|
||
|
printk("Params: %x %x %x %x %x %x %x\n", params[0], params[1],
|
||
|
params[2], params[3], params[4], params[5], params[6]);
|
||
|
printk("Sony CDROM error 0x%2.2x (CDROMPLAYTRKIND\n", res_reg[1]);
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
/* Save the final position for pauses and resumes */
|
||
|
final_pos_msf[0] = params[4];
|
||
|
final_pos_msf[1] = params[5];
|
||
|
final_pos_msf[2] = params[6];
|
||
|
sony_audio_status = CDROM_AUDIO_PLAY;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
case CDROMSUBCHNL: /* Get subchannel info */
|
||
|
return sony_get_subchnl_info(arg);
|
||
|
|
||
|
case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */
|
||
|
{
|
||
|
struct cdrom_volctrl volctrl;
|
||
|
|
||
|
verify_area(VERIFY_READ, (char *) arg, sizeof(volctrl));
|
||
|
|
||
|
memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
|
||
|
params[0] = SONY_SD_AUDIO_VOLUME;
|
||
|
params[1] = volctrl.channel0;
|
||
|
params[2] = volctrl.channel1;
|
||
|
do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, params, 3, res_reg, &res_size);
|
||
|
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
|
||
|
{
|
||
|
printk("Sony CDROM error 0x%2.2x (CDROMVOLCTRL)\n", res_reg[1]);
|
||
|
return -EIO;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
case CDROMEJECT: /* Eject the drive */
|
||
|
do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
|
||
|
do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
|
||
|
|
||
|
sony_audio_status = CDROM_AUDIO_INVALID;
|
||
|
do_sony_cd_cmd(SONY_EJECT_CMD, NULL, 0, res_reg, &res_size);
|
||
|
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
|
||
|
{
|
||
|
printk("Sony CDROM error 0x%2.2x (CDROMEJECT)\n", res_reg[1]);
|
||
|
return -EIO;
|
||
|
}
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Open the drive for operations. Spin the drive up and read the table of
|
||
|
* contents if these have not already been done.
|
||
|
*/
|
||
|
static int
|
||
|
scd_open(struct inode *inode,
|
||
|
struct file *filp)
|
||
|
{
|
||
|
unsigned char res_reg[2];
|
||
|
unsigned int res_size;
|
||
|
int num_spin_ups;
|
||
|
|
||
|
|
||
|
if (!sony_spun_up)
|
||
|
{
|
||
|
num_spin_ups = 0;
|
||
|
|
||
|
respinup_on_open:
|
||
|
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
|
||
|
|
||
|
/* The drive sometimes returns error 0. I don't know why, but ignore
|
||
|
it. It seems to mean the drive has already done the operation. */
|
||
|
if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
|
||
|
{
|
||
|
printk("Sony CDROM error 0x%2.2x (scd_open, spin up)\n", res_reg[1]);
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
|
||
|
|
||
|
/* The drive sometimes returns error 0. I don't know why, but ignore
|
||
|
it. It seems to mean the drive has already done the operation. */
|
||
|
if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
|
||
|
{
|
||
|
/* If the drive is already playing, its ok. */
|
||
|
if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0))
|
||
|
{
|
||
|
goto drive_spinning;
|
||
|
}
|
||
|
|
||
|
/* If the drive says it is not spun up (even though we just did it!)
|
||
|
then retry the operation at least a few times. */
|
||
|
if ( (res_reg[1] == SONY_NOT_SPIN_ERR)
|
||
|
&& (num_spin_ups < MAX_CDU31A_RETRIES))
|
||
|
{
|
||
|
num_spin_ups++;
|
||
|
goto respinup_on_open;
|
||
|
}
|
||
|
|
||
|
printk("Sony CDROM error 0x%2.2x (scd_open, read toc)\n", res_reg[1]);
|
||
|
do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
|
||
|
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
sony_get_toc();
|
||
|
if (!sony_toc_read)
|
||
|
{
|
||
|
do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
|
||
|
return -EIO;
|
||
|
}
|
||
|
|
||
|
sony_spun_up = 1;
|
||
|
}
|
||
|
|
||
|
drive_spinning:
|
||
|
|
||
|
if (inode)
|
||
|
{
|
||
|
check_disk_change(inode->i_rdev);
|
||
|
}
|
||
|
|
||
|
sony_usage++;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Close the drive. Spin it down if no task is using it. The spin
|
||
|
* down will fail if playing audio, so audio play is OK.
|
||
|
*/
|
||
|
static void
|
||
|
scd_release(struct inode *inode,
|
||
|
struct file *filp)
|
||
|
{
|
||
|
unsigned char res_reg[2];
|
||
|
unsigned int res_size;
|
||
|
|
||
|
|
||
|
if (sony_usage > 0)
|
||
|
{
|
||
|
sony_usage--;
|
||
|
}
|
||
|
if (sony_usage == 0)
|
||
|
{
|
||
|
sync_dev(inode->i_rdev);
|
||
|
do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
|
||
|
|
||
|
sony_spun_up = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static struct file_operations scd_fops = {
|
||
|
NULL, /* lseek - default */
|
||
|
block_read, /* read - general block-dev read */
|
||
|
block_write, /* write - general block-dev write */
|
||
|
NULL, /* readdir - bad */
|
||
|
NULL, /* select */
|
||
|
scd_ioctl, /* ioctl */
|
||
|
NULL, /* mmap */
|
||
|
scd_open, /* open */
|
||
|
scd_release, /* release */
|
||
|
NULL /* fsync */
|
||
|
};
|
||
|
|
||
|
|
||
|
/* The different types of disc loading mechanisms supported */
|
||
|
static char *load_mech[] = { "caddy", "tray", "pop-up", "unknown" };
|
||
|
|
||
|
/* Read-ahead buffer sizes for different drives. These are just arbitrary
|
||
|
values, I don't know what is really optimum. */
|
||
|
static unsigned int mem_size[] = { 16384, 16384, 16384, 2048 };
|
||
|
|
||
|
void
|
||
|
get_drive_configuration(unsigned short base_io,
|
||
|
unsigned char res_reg[],
|
||
|
unsigned int *res_size)
|
||
|
{
|
||
|
int retry_count;
|
||
|
|
||
|
|
||
|
/* Set the base address */
|
||
|
sony_cd_base_io = base_io;
|
||
|
|
||
|
/* Set up all the register locations */
|
||
|
sony_cd_cmd_reg = sony_cd_base_io + SONY_CMD_REG_OFFSET;
|
||
|
sony_cd_param_reg = sony_cd_base_io + SONY_PARAM_REG_OFFSET;
|
||
|
sony_cd_write_reg = sony_cd_base_io + SONY_WRITE_REG_OFFSET;
|
||
|
sony_cd_control_reg = sony_cd_base_io + SONY_CONTROL_REG_OFFSET;
|
||
|
sony_cd_status_reg = sony_cd_base_io + SONY_STATUS_REG_OFFSET;
|
||
|
sony_cd_result_reg = sony_cd_base_io + SONY_RESULT_REG_OFFSET;
|
||
|
sony_cd_read_reg = sony_cd_base_io + SONY_READ_REG_OFFSET;
|
||
|
sony_cd_fifost_reg = sony_cd_base_io + SONY_FIFOST_REG_OFFSET;
|
||
|
|
||
|
/*
|
||
|
* Check to see if anything exists at the status register location.
|
||
|
* I don't know if this is a good way to check, but it seems to work
|
||
|
* ok for me.
|
||
|
*/
|
||
|
if (read_status_register() != 0xff)
|
||
|
{
|
||
|
/*
|
||
|
* Reset the drive and wait for attention from it (to say its reset).
|
||
|
* If you don't wait, the next operation will probably fail.
|
||
|
*/
|
||
|
reset_drive();
|
||
|
retry_count = jiffies + SONY_RESET_TIMEOUT;
|
||
|
while ((retry_count > jiffies) && (!is_attention()))
|
||
|
{
|
||
|
sony_sleep();
|
||
|
}
|
||
|
|
||
|
/* If attention is never seen probably not a CDU31a present */
|
||
|
if (!is_attention())
|
||
|
{
|
||
|
res_reg[0] = 0x20;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get the drive configuration.
|
||
|
*/
|
||
|
do_sony_cd_cmd(SONY_REQ_DRIVE_CONFIG_CMD,
|
||
|
NULL,
|
||
|
0,
|
||
|
(unsigned char *) res_reg,
|
||
|
res_size);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Return an error */
|
||
|
res_reg[0] = 0x20;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Initialize the driver.
|
||
|
*/
|
||
|
unsigned long
|
||
|
cdu31a_init(unsigned long mem_start, unsigned long mem_end)
|
||
|
{
|
||
|
struct s_sony_drive_config drive_config;
|
||
|
unsigned int res_size;
|
||
|
int i;
|
||
|
int drive_found;
|
||
|
|
||
|
|
||
|
/*
|
||
|
* According to Alex Freed (freed@europa.orion.adobe.com), this is
|
||
|
* required for the Fusion CD-16 package. If the sound driver is
|
||
|
* loaded, it should work fine, but just in case...
|
||
|
*
|
||
|
* The following turn on the CD-ROM interface for a Fusion CD-16.
|
||
|
*/
|
||
|
outb(0xbc, 0x9a01);
|
||
|
outb(0xe2, 0x9a01);
|
||
|
|
||
|
i = 0;
|
||
|
drive_found = 0;
|
||
|
while ( (cdu31a_addresses[i] != 0)
|
||
|
&& (!drive_found))
|
||
|
{
|
||
|
if (check_region(cdu31a_addresses[i], 4)) {
|
||
|
i++;
|
||
|
continue;
|
||
|
}
|
||
|
get_drive_configuration(cdu31a_addresses[i],
|
||
|
drive_config.exec_status,
|
||
|
&res_size);
|
||
|
if ((res_size > 2) && ((drive_config.exec_status[0] & 0x20) == 0x00))
|
||
|
{
|
||
|
drive_found = 1;
|
||
|
snarf_region(cdu31a_addresses[i], 4);
|
||
|
|
||
|
if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops))
|
||
|
{
|
||
|
printk("Unable to get major %d for CDU-31a\n", MAJOR_NR);
|
||
|
return mem_start;
|
||
|
}
|
||
|
|
||
|
sony_buffer_size = mem_size[SONY_HWC_GET_BUF_MEM_SIZE(drive_config)];
|
||
|
sony_buffer_sectors = sony_buffer_size / 2048;
|
||
|
|
||
|
printk("Sony I/F CDROM : %8.8s %16.16s %8.8s with %s load mechanism\n",
|
||
|
drive_config.vendor_id,
|
||
|
drive_config.product_id,
|
||
|
drive_config.product_rev_level,
|
||
|
load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]);
|
||
|
printk(" using %d byte buffer", sony_buffer_size);
|
||
|
if (SONY_HWC_AUDIO_PLAYBACK(drive_config))
|
||
|
{
|
||
|
printk(", capable of audio playback");
|
||
|
}
|
||
|
printk("\n");
|
||
|
|
||
|
set_drive_params();
|
||
|
|
||
|
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
|
||
|
read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */
|
||
|
|
||
|
sony_toc = (struct s_sony_toc *) mem_start;
|
||
|
mem_start += sizeof(*sony_toc);
|
||
|
last_sony_subcode = (struct s_sony_subcode *) mem_start;
|
||
|
mem_start += sizeof(*last_sony_subcode);
|
||
|
sony_buffer = (unsigned char *) mem_start;
|
||
|
mem_start += sony_buffer_size;
|
||
|
}
|
||
|
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
return mem_start;
|
||
|
}
|
||
|
|