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