📄 cdu31a.c
字号:
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;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -