📄 sjcd.c
字号:
#if defined( SJCD_TRACE )
printk("SJCD: (%02x:%02x.%02x)\n", sjcd_disk_length.min,
sjcd_disk_length.sec, sjcd_disk_length.frame);
#endif
return (0);
}
/*
* Load subchannel information.
*/
static int sjcd_get_q_info(struct sjcd_hw_qinfo *qp)
{
int s;
#if defined( SJCD_TRACE )
printk("SJCD: load sub q\n");
#endif
sjcd_send_cmd(SCMD_GET_QINFO);
s = sjcd_receive_status();
if (s < 0 || sjcd_command_failed || !sjcd_status_valid) {
sjcd_send_cmd(0xF2);
s = sjcd_receive_status();
if (s < 0 || sjcd_command_failed || !sjcd_status_valid)
return (-1);
sjcd_send_cmd(SCMD_GET_QINFO);
s = sjcd_receive_status();
if (s < 0 || sjcd_command_failed || !sjcd_status_valid)
return (-1);
}
if (sjcd_media_is_available)
if (sjcd_load_response(qp, sizeof(*qp)) == 0)
return (0);
return (-1);
}
/*
* Start playing from the specified position.
*/
static int sjcd_play(struct sjcd_play_msf *mp)
{
struct sjcd_play_msf msf;
/*
* Turn the device to play mode.
*/
sjcd_send_1_cmd(SCMD_SET_MODE, SCMD_MODE_PLAY);
if (sjcd_receive_status() < 0)
return (-1);
/*
* Seek to the starting point.
*/
msf.start = mp->start;
msf.end.min = msf.end.sec = msf.end.frame = 0x00;
sjcd_send_6_cmd(SCMD_SEEK, &msf);
if (sjcd_receive_status() < 0)
return (-1);
/*
* Start playing.
*/
sjcd_send_6_cmd(SCMD_PLAY, mp);
return (sjcd_receive_status());
}
/*
* Tray control functions.
*/
static int sjcd_tray_close(void)
{
#if defined( SJCD_TRACE )
printk("SJCD: tray_close\n");
#endif
sjcd_send_cmd(SCMD_CLOSE_TRAY);
return (sjcd_receive_status());
}
static int sjcd_tray_lock(void)
{
#if defined( SJCD_TRACE )
printk("SJCD: tray_lock\n");
#endif
sjcd_send_cmd(SCMD_LOCK_TRAY);
return (sjcd_receive_status());
}
static int sjcd_tray_unlock(void)
{
#if defined( SJCD_TRACE )
printk("SJCD: tray_unlock\n");
#endif
sjcd_send_cmd(SCMD_UNLOCK_TRAY);
return (sjcd_receive_status());
}
static int sjcd_tray_open(void)
{
#if defined( SJCD_TRACE )
printk("SJCD: tray_open\n");
#endif
sjcd_send_cmd(SCMD_EJECT_TRAY);
return (sjcd_receive_status());
}
/*
* Do some user commands.
*/
static int sjcd_ioctl(struct inode *ip, struct file *fp,
unsigned int cmd, unsigned long arg)
{
#if defined( SJCD_TRACE )
printk("SJCD:ioctl\n");
#endif
if (ip == NULL)
return (-EINVAL);
sjcd_get_status();
if (!sjcd_status_valid)
return (-EIO);
if (sjcd_update_toc() < 0)
return (-EIO);
switch (cmd) {
case CDROMSTART:{
#if defined( SJCD_TRACE )
printk("SJCD: ioctl: start\n");
#endif
return (0);
}
case CDROMSTOP:{
#if defined( SJCD_TRACE )
printk("SJCD: ioctl: stop\n");
#endif
sjcd_send_cmd(SCMD_PAUSE);
(void) sjcd_receive_status();
sjcd_audio_status = CDROM_AUDIO_NO_STATUS;
return (0);
}
case CDROMPAUSE:{
struct sjcd_hw_qinfo q_info;
#if defined( SJCD_TRACE )
printk("SJCD: ioctl: pause\n");
#endif
if (sjcd_audio_status == CDROM_AUDIO_PLAY) {
sjcd_send_cmd(SCMD_PAUSE);
(void) sjcd_receive_status();
if (sjcd_get_q_info(&q_info) < 0) {
sjcd_audio_status =
CDROM_AUDIO_NO_STATUS;
} else {
sjcd_audio_status =
CDROM_AUDIO_PAUSED;
sjcd_playing.start = q_info.abs;
}
return (0);
} else
return (-EINVAL);
}
case CDROMRESUME:{
#if defined( SJCD_TRACE )
printk("SJCD: ioctl: resume\n");
#endif
if (sjcd_audio_status == CDROM_AUDIO_PAUSED) {
/*
* continue play starting at saved location
*/
if (sjcd_play(&sjcd_playing) < 0) {
sjcd_audio_status =
CDROM_AUDIO_ERROR;
return (-EIO);
} else {
sjcd_audio_status =
CDROM_AUDIO_PLAY;
return (0);
}
} else
return (-EINVAL);
}
case CDROMPLAYTRKIND:{
struct cdrom_ti ti;
int s;
#if defined( SJCD_TRACE )
printk("SJCD: ioctl: playtrkind\n");
#endif
if ((s =
verify_area(VERIFY_READ, (void *) arg,
sizeof(ti))) == 0) {
copy_from_user(&ti, (void *) arg,
sizeof(ti));
if (ti.cdti_trk0 < sjcd_first_track_no)
return (-EINVAL);
if (ti.cdti_trk1 > sjcd_last_track_no)
ti.cdti_trk1 = sjcd_last_track_no;
if (ti.cdti_trk0 > ti.cdti_trk1)
return (-EINVAL);
sjcd_playing.start =
sjcd_table_of_contents[ti.cdti_trk0].
un.track_msf;
sjcd_playing.end =
(ti.cdti_trk1 <
sjcd_last_track_no) ?
sjcd_table_of_contents[ti.cdti_trk1 +
1].un.
track_msf : sjcd_table_of_contents[0].
un.track_msf;
if (sjcd_play(&sjcd_playing) < 0) {
sjcd_audio_status =
CDROM_AUDIO_ERROR;
return (-EIO);
} else
sjcd_audio_status =
CDROM_AUDIO_PLAY;
}
return (s);
}
case CDROMPLAYMSF:{
struct cdrom_msf sjcd_msf;
int s;
#if defined( SJCD_TRACE )
printk("SJCD: ioctl: playmsf\n");
#endif
if ((s =
verify_area(VERIFY_READ, (void *) arg,
sizeof(sjcd_msf))) == 0) {
if (sjcd_audio_status == CDROM_AUDIO_PLAY) {
sjcd_send_cmd(SCMD_PAUSE);
(void) sjcd_receive_status();
sjcd_audio_status =
CDROM_AUDIO_NO_STATUS;
}
copy_from_user(&sjcd_msf, (void *) arg,
sizeof(sjcd_msf));
sjcd_playing.start.min =
bin2bcd(sjcd_msf.cdmsf_min0);
sjcd_playing.start.sec =
bin2bcd(sjcd_msf.cdmsf_sec0);
sjcd_playing.start.frame =
bin2bcd(sjcd_msf.cdmsf_frame0);
sjcd_playing.end.min =
bin2bcd(sjcd_msf.cdmsf_min1);
sjcd_playing.end.sec =
bin2bcd(sjcd_msf.cdmsf_sec1);
sjcd_playing.end.frame =
bin2bcd(sjcd_msf.cdmsf_frame1);
if (sjcd_play(&sjcd_playing) < 0) {
sjcd_audio_status =
CDROM_AUDIO_ERROR;
return (-EIO);
} else
sjcd_audio_status =
CDROM_AUDIO_PLAY;
}
return (s);
}
case CDROMREADTOCHDR:{
struct cdrom_tochdr toc_header;
int s;
#if defined (SJCD_TRACE )
printk("SJCD: ioctl: readtocheader\n");
#endif
if ((s =
verify_area(VERIFY_WRITE, (void *) arg,
sizeof(toc_header))) == 0) {
toc_header.cdth_trk0 = sjcd_first_track_no;
toc_header.cdth_trk1 = sjcd_last_track_no;
copy_to_user((void *) arg, &toc_header,
sizeof(toc_header));
}
return (s);
}
case CDROMREADTOCENTRY:{
struct cdrom_tocentry toc_entry;
int s;
#if defined( SJCD_TRACE )
printk("SJCD: ioctl: readtocentry\n");
#endif
if ((s =
verify_area(VERIFY_WRITE, (void *) arg,
sizeof(toc_entry))) == 0) {
struct sjcd_hw_disk_info *tp;
copy_from_user(&toc_entry, (void *) arg,
sizeof(toc_entry));
if (toc_entry.cdte_track == CDROM_LEADOUT)
tp = &sjcd_table_of_contents[0];
else if (toc_entry.cdte_track <
sjcd_first_track_no)
return (-EINVAL);
else if (toc_entry.cdte_track >
sjcd_last_track_no)
return (-EINVAL);
else
tp = &sjcd_table_of_contents
[toc_entry.cdte_track];
toc_entry.cdte_adr =
tp->track_control & 0x0F;
toc_entry.cdte_ctrl =
tp->track_control >> 4;
switch (toc_entry.cdte_format) {
case CDROM_LBA:
toc_entry.cdte_addr.lba =
msf2hsg(&(tp->un.track_msf));
break;
case CDROM_MSF:
toc_entry.cdte_addr.msf.minute =
bcd2bin(tp->un.track_msf.min);
toc_entry.cdte_addr.msf.second =
bcd2bin(tp->un.track_msf.sec);
toc_entry.cdte_addr.msf.frame =
bcd2bin(tp->un.track_msf.
frame);
break;
default:
return (-EINVAL);
}
copy_to_user((void *) arg, &toc_entry,
sizeof(toc_entry));
}
return (s);
}
case CDROMSUBCHNL:{
struct cdrom_subchnl subchnl;
int s;
#if defined( SJCD_TRACE )
printk("SJCD: ioctl: subchnl\n");
#endif
if ((s =
verify_area(VERIFY_WRITE, (void *) arg,
sizeof(subchnl))) == 0) {
struct sjcd_hw_qinfo q_info;
copy_from_user(&subchnl, (void *) arg,
sizeof(subchnl));
if (sjcd_get_q_info(&q_info) < 0)
return (-EIO);
subchnl.cdsc_audiostatus =
sjcd_audio_status;
subchnl.cdsc_adr =
q_info.track_control & 0x0F;
subchnl.cdsc_ctrl =
q_info.track_control >> 4;
subchnl.cdsc_trk =
bcd2bin(q_info.track_no);
subchnl.cdsc_ind = bcd2bin(q_info.x);
switch (subchnl.cdsc_format) {
case CDROM_LBA:
subchnl.cdsc_absaddr.lba =
msf2hsg(&(q_info.abs));
subchnl.cdsc_reladdr.lba =
msf2hsg(&(q_info.rel));
break;
case CDROM_MSF:
subchnl.cdsc_absaddr.msf.minute =
bcd2bin(q_info.abs.min);
subchnl.cdsc_absaddr.msf.second =
bcd2bin(q_info.abs.sec);
subchnl.cdsc_absaddr.msf.frame =
bcd2bin(q_info.abs.frame);
subchnl.cdsc_reladdr.msf.minute =
bcd2bin(q_info.rel.min);
subchnl.cdsc_reladdr.msf.second =
bcd2bin(q_info.rel.sec);
subchnl.cdsc_reladdr.msf.frame =
bcd2bin(q_info.rel.frame);
break;
default:
return (-EINVAL);
}
copy_to_user((void *) arg, &subchnl,
sizeof(subchnl));
}
return (s);
}
case CDROMVOLCTRL:{
struct cdrom_volctrl vol_ctrl;
int s;
#if defined( SJCD_TRACE )
printk("SJCD: ioctl: volctrl\n");
#endif
if ((s =
verify_area(VERIFY_READ, (void *) arg,
sizeof(vol_ctrl))) == 0) {
unsigned char dummy[4];
copy_from_user(&vol_ctrl, (void *) arg,
sizeof(vol_ctrl));
sjcd_send_4_cmd(SCMD_SET_VOLUME,
vol_ctrl.channel0, 0xFF,
vol_ctrl.channel1, 0xFF);
if (sjcd_receive_status() < 0)
return (-EIO);
(void) sjcd_load_response(dummy, 4);
}
return (s);
}
case CDROMEJECT:{
#if defined( SJCD_TRACE )
printk("SJCD: ioctl: eject\n");
#endif
if (!sjcd_command_is_in_progress) {
sjcd_tray_unlock();
sjcd_send_cmd(SCMD_EJECT_TRAY);
(void) sjcd_receive_status();
}
return (0);
}
#if defined( SJCD_GATHER_STAT )
case 0xABCD:{
int s;
#if defined( SJCD_TRACE )
printk("SJCD: ioctl: statistic\n");
#endif
if ((s =
verify_area(VERIFY_WRITE, (void *) arg,
sizeof(statistic))) == 0)
copy_to_user((void *) arg, &statistic,
sizeof(statistic));
return (s);
}
#endif
default:
return (-EINVAL);
}
}
/*
* Invalidate internal buffers of the driver.
*/
static void sjcd_invalidate_buffers(void)
{
int i;
for (i = 0; i < SJCD_BUF_SIZ; sjcd_buf_bn[i++] = -1);
sjcd_buf_out = -1;
}
/*
* Take care of the different block sizes between cdrom and Linux.
* When Linux gets variable block sizes this will probably go away.
*/
#define CURRENT_IS_VALID \
( !QUEUE_EMPTY && MAJOR( CURRENT->rq_dev ) == MAJOR_NR && \
CURRENT->cmd == READ && CURRENT->sector != -1 )
static void sjcd_transfer(void)
{
#if defined( SJCD_TRACE )
printk("SJCD: transfer:\n");
#endif
if (CURRENT_IS_VALID) {
while (CURRENT->nr_sectors) {
int i, bn = CURRENT->sector / 4;
for (i = 0;
i < SJCD_BUF_SIZ && sjcd_buf_bn[i] != bn;
i++);
if (i < SJCD_BUF_SIZ) {
int offs =
(i * 4 + (CURRENT->sector & 3)) * 512;
int nr_sectors = 4 - (CURRENT->sector & 3);
if (sjcd_buf_out != i) {
sjcd_buf_out = i;
if (sjcd_buf_bn[i] != bn) {
sjcd_buf_out = -1;
continue;
}
}
if (nr_sectors > CURRENT->nr_sectors)
nr_sectors = CURRENT->nr_sectors;
#if defined( SJCD_TRACE )
printk("SJCD: copy out\n");
#endif
memcpy(CURRENT->buffer, sjcd_buf + offs,
nr_sectors * 512);
CURRENT->nr_sectors -= nr_sectors;
CURRENT->sector += nr_sectors;
CURRENT->buffer += nr_sectors * 512;
} else {
sjcd_buf_out = -1;
break;
}
}
}
#if defined( SJCD_TRACE )
printk("SJCD: transfer: done\n");
#endif
}
static void sjcd_poll(void)
{
#if defined( SJCD_GATHER_STAT )
/*
* Update total number of ticks.
*/
statistic.ticks++;
statistic.tticks[sjcd_transfer_state]++;
#endif
ReSwitch:switch (sjcd_transfer_state) {
case SJCD_S_IDLE:{
#if defined( SJCD_GATHER_STAT )
statistic.idle_ticks++;
#endif
#if defined( SJCD_TRACE )
printk("SJCD_S_IDLE\n");
#endif
return;
}
case SJCD_S_START:{
#if defined( SJCD_GATHER_STAT )
statistic.start_ticks++;
#endif
sjcd_send_cmd(SCMD_GET_STATUS);
sjcd_transfer_state =
sjcd_mode ==
SCMD_MODE_COOKED ? SJCD_S_READ : SJCD_S_MODE;
sjcd_transfer_timeout = 500;
#if defined( SJCD_TRACE )
printk("SJCD_S_START: goto SJCD_S_%s mode\n",
sjcd_transfer_state ==
SJCD_S_READ ? "READ" : "MODE");
#endif
break;
}
case SJCD_S_MODE:{
if (sjcd_check_status()) {
/*
* Previous command is completed.
*/
if (!sjcd_status_valid
|| sjcd_command_failed) {
#if defined( SJCD_TRACE )
printk
("SJCD_S_MODE: pre-cmd failed: goto to SJCD_S_STOP mode\n");
#endif
sjcd_transfer_state = SJCD_S_STOP;
goto ReSwitch;
}
sjcd_mode = 0; /* unknown mode; should not be valid when failed */
sjcd_send_1_cmd(SCMD_SET_MODE,
SCMD_MODE_COOKED);
sjcd_transfer_state = SJCD_S_READ;
sjcd_transfer_timeout = 1000;
#if defined( SJCD_TRACE )
printk
("SJCD_S_MODE: goto SJCD_S_READ mode\n");
#endif
}
#if defined( SJCD_GATHER_STAT )
else
statistic.mode_ticks++;
#endif
break;
}
case SJCD_S_READ:{
if (sjcd_status_valid ? 1 : sjcd_check_status()) {
/*
* Previous command is completed.
*/
if (!sjcd_status_valid
|| sjcd_command_failed) {
#if defined( SJCD_TRACE )
printk
("SJCD_S_READ: pre-cmd failed: goto to SJCD_S_STOP mode\n");
#endif
sjcd_transfer_state = SJCD_S_STOP;
goto ReSwitch;
}
if (!sjcd_media_is_available) {
#if defined( SJCD_TRACE )
printk
("SJCD_S_READ: no disk: goto to SJCD_S_STOP mode\n");
#endif
sjcd_transfer_state = SJCD_S_STOP;
goto ReSwitch;
}
if (sjcd_mode != SCMD_MODE_COOKED) {
/*
* We seem to come from set mode. So discard one byte of result.
*/
if (sjcd_load_response
(&sjcd_mode, 1) != 0) {
#if defined( SJCD_TRACE )
printk
("SJCD_S_READ: load failed: goto to SJCD_S_STOP mode\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -