📄 sonycd535.c
字号:
* spin it up and try some more.
*/
for (spin_up_retry=0 ;; ++spin_up_retry) {
/* This loop has been modified to support the Sony
* CDU-510/515 series, thanks to Claudio Porfiri
* <C.Porfiri@nisms.tei.ericsson.se>.
*/
/*
* This part is to deal with very slow hardware. We
* try at most MAX_SPINUP_RETRY times to read the same
* block. A check for seek_and_read_N_blocks' result is
* performed; if the result is wrong, the CDROM's engine
* is restarted and the operation is tried again.
*/
/*
* 1995-06-01: The system got problems when downloading
* from Slackware CDROM, the problem seems to be:
* seek_and_read_N_blocks returns BAD_STATUS and we
* should wait for a while before retrying, so a new
* part was added to discriminate the return value from
* seek_and_read_N_blocks for the various cases.
*/
int readStatus = seek_and_read_N_blocks(params, read_size,
status, sony_buffer, (read_size * CDU535_BLOCK_SIZE));
if (0 <= readStatus) /* Good data; common case, placed first */
break;
if (readStatus == NO_ROOM || spin_up_retry == MAX_SPINUP_RETRY) {
/* give up */
if (readStatus == NO_ROOM)
printk(CDU535_MESSAGE_NAME " No room to read from CD\n");
else
printk(CDU535_MESSAGE_NAME " Read error: 0x%.2x\n",
status[0]);
sony_first_block = -1;
sony_last_block = -1;
end_request(0);
return;
}
if (readStatus == BAD_STATUS) {
/* Sleep for a while, then retry */
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(RETRY_FOR_BAD_STATUS*HZ/10);
}
#if DEBUG > 0
printk(CDU535_MESSAGE_NAME
" debug: calling spin up when reading data!\n");
#endif
cmd[0] = SONY535_SPIN_UP;
do_sony_cmd(cmd, 1, status, NULL, 0, 0);
}
}
/*
* The data is in memory now, copy it to the buffer and advance to the
* next block to read.
*/
copyoff = block - sony_first_block;
memcpy(CURRENT->buffer,
sony_buffer[copyoff / 4] + 512 * (copyoff % 4), 512);
block += 1;
nsect -= 1;
CURRENT->buffer += 512;
}
end_request(1);
break;
case WRITE:
end_request(0);
break;
default:
panic("Unknown SONY CD cmd");
}
}
}
/*
* Read the table of contents from the drive and set sony_toc_read if
* successful.
*/
static void
sony_get_toc(void)
{
Byte status[2];
if (!sony_toc_read) {
/* do not call check_drive_status() from here since it can call this routine */
if (request_toc_data(status, sony_toc) < 0)
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. track is
* passed in bcd format
*/
static int
find_track(int track)
{
int i;
int num_tracks;
num_tracks = bcd_to_int(sony_toc->last_track_num) -
bcd_to_int(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)
{
Byte cmd = SONY535_REQUEST_SUB_Q_DATA;
Byte status[2];
int dsc_status;
if (check_drive_status() != 0)
return -EIO;
if ((dsc_status = do_sony_cmd(&cmd, 1, status, (Byte *) last_sony_subcode,
sizeof(struct s535_sony_subcode), 1)) != 0) {
printk(CDU535_MESSAGE_NAME " error 0x%.2x, %d (read_subcode)\n",
status[0], dsc_status);
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;
int err;
/* Get attention stuff */
if (check_drive_status() != 0)
return -EIO;
sony_get_toc();
if (!sony_toc_read) {
return -EIO;
}
err = verify_area(VERIFY_WRITE /* and read */ , (char *)arg, sizeof schi);
if (err)
return err;
copy_from_user(&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;
copy_to_user((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);
}
copy_to_user((char *)arg, &schi, sizeof schi);
return 0;
}
/*
* The big ugly ioctl handler.
*/
static int
cdu_ioctl(struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
unsigned int dev;
Byte status[2];
Byte cmd_buff[10], params[10];
int i;
int dsc_status;
int err;
if (!inode) {
return -EINVAL;
}
dev = MINOR(inode->i_rdev) >> 6;
if (dev != 0) {
return -EINVAL;
}
if (check_drive_status() != 0)
return -EIO;
switch (cmd) {
case CDROMSTART: /* Spin up the drive */
if (spin_up_drive(status) < 0) {
printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTART)\n",
status[0]);
return -EIO;
}
return 0;
break;
case CDROMSTOP: /* Spin down the drive */
cmd_buff[0] = SONY535_HOLD;
do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
/*
* Spin the drive down, ignoring the error if the disk was
* already not spinning.
*/
sony_audio_status = CDROM_AUDIO_NO_STATUS;
cmd_buff[0] = SONY535_SPIN_DOWN;
dsc_status = do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
if (((dsc_status < 0) && (dsc_status != BAD_STATUS)) ||
((status[0] & ~(SONY535_STATUS1_NOT_SPINNING)) != 0)) {
printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTOP)\n",
status[0]);
return -EIO;
}
return 0;
break;
case CDROMPAUSE: /* Pause the drive */
cmd_buff[0] = SONY535_HOLD; /* CDU-31 driver uses AUDIO_STOP, not pause */
if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) {
printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPAUSE)\n",
status[0]);
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 */
set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
if (sony_audio_status != CDROM_AUDIO_PAUSED) {
return -EINVAL;
}
spin_up_drive(status);
/* Start the drive at the saved position. */
cmd_buff[0] = SONY535_PLAY_AUDIO;
cmd_buff[1] = 0; /* play back starting at this address */
cmd_buff[2] = cur_pos_msf[0];
cmd_buff[3] = cur_pos_msf[1];
cmd_buff[4] = cur_pos_msf[2];
cmd_buff[5] = SONY535_PLAY_AUDIO;
cmd_buff[6] = 2; /* set ending address */
cmd_buff[7] = final_pos_msf[0];
cmd_buff[8] = final_pos_msf[1];
cmd_buff[9] = final_pos_msf[2];
if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
(do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMRESUME)\n",
status[0]);
return -EIO;
}
sony_audio_status = CDROM_AUDIO_PLAY;
return 0;
break;
case CDROMPLAYMSF: /* Play starting at the given MSF address. */
err = verify_area(VERIFY_READ, (char *)arg, 6);
if (err)
return err;
spin_up_drive(status);
set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
copy_from_user(params, (void *)arg, 6);
/* The parameters are given in int, must be converted */
for (i = 0; i < 3; i++) {
cmd_buff[2 + i] = int_to_bcd(params[i]);
cmd_buff[7 + i] = int_to_bcd(params[i + 3]);
}
cmd_buff[0] = SONY535_PLAY_AUDIO;
cmd_buff[1] = 0; /* play back starting at this address */
/* cmd_buff[2-4] are filled in for loop above */
cmd_buff[5] = SONY535_PLAY_AUDIO;
cmd_buff[6] = 2; /* set ending address */
/* cmd_buff[7-9] are filled in for loop above */
if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
(do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYMSF)\n",
status[0]);
return -EIO;
}
/* Save the final position for pauses and resumes */
final_pos_msf[0] = cmd_buff[7];
final_pos_msf[1] = cmd_buff[8];
final_pos_msf[2] = cmd_buff[9];
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;
err = verify_area(VERIFY_WRITE, hdr, sizeof *hdr);
if (err)
return err;
loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num);
loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num);
copy_to_user(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;
Byte *msf_val = NULL;
sony_get_toc();
if (!sony_toc_read) {
return -EIO;
}
entry = (struct cdrom_tocentry *)arg;
err = verify_area(VERIFY_WRITE /* and read */ , entry, sizeof *entry);
if (err)
return err;
copy_from_user(&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 = 0 /*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 = 0 /*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));
}
copy_to_user(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;
err = verify_area(VERIFY_READ, (char *)arg, sizeof ti);
if (err)
return err;
copy_from_user(&ti, (char *)arg, sizeof ti);
if ((ti.cdti_trk0 < sony_toc->first_track_num)
|| (sony_toc->last_track_num < ti.cdti_trk0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -