📄 sonycd535.c
字号:
|| (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 (bcd_to_int(sony_toc->last_track_num) <= ti.cdti_trk1) {
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;
spin_up_drive(status);
set_drive_mode(SONY535_AUDIO_DRIVE_MODE, 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] = params[1];
cmd_buff[3] = params[2];
cmd_buff[4] = params[3];
cmd_buff[5] = SONY535_PLAY_AUDIO;
cmd_buff[6] = 2; /* set ending address */
cmd_buff[7] = params[4];
cmd_buff[8] = params[5];
cmd_buff[9] = params[6];
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 (CDROMPLAYTRKIND)\n",
status[0]);
printk("... Params: %x %x %x %x %x %x %x\n",
params[0], params[1], params[2],
params[3], params[4], params[5], params[6]);
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;
err = verify_area(VERIFY_READ, (char *)arg, sizeof volctrl);
if (err)
return err;
copy_from_user(&volctrl, (char *)arg, sizeof volctrl);
cmd_buff[0] = SONY535_SET_VOLUME;
cmd_buff[1] = volctrl.channel0;
cmd_buff[2] = volctrl.channel1;
if (do_sony_cmd(cmd_buff, 3, status, NULL, 0, 0) != 0) {
printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMVOLCTRL)\n",
status[0]);
return -EIO;
}
}
return 0;
case CDROMEJECT: /* Eject the drive */
cmd_buff[0] = SONY535_STOP;
do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
cmd_buff[0] = SONY535_SPIN_DOWN;
do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
sony_audio_status = CDROM_AUDIO_INVALID;
cmd_buff[0] = SONY535_EJECT_CADDY;
if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) {
printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMEJECT)\n",
status[0]);
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
cdu_open(struct inode *inode,
struct file *filp)
{
Byte status[2], cmd_buff[2];
if (sony_inuse)
return -EBUSY;
if (check_drive_status() != 0)
return -EIO;
sony_inuse = 1;
if (spin_up_drive(status) != 0) {
printk(CDU535_MESSAGE_NAME " error 0x%.2x (cdu_open, spin up)\n",
status[0]);
sony_inuse = 0;
return -EIO;
}
sony_get_toc();
if (!sony_toc_read) {
cmd_buff[0] = SONY535_SPIN_DOWN;
do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
sony_inuse = 0;
return -EIO;
}
if (inode) {
check_disk_change(inode->i_rdev);
}
sony_usage++;
#ifdef LOCK_DOORS
/* disable the eject button while mounted */
cmd_buff[0] = SONY535_DISABLE_EJECT_BUTTON;
do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
#endif
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 int
cdu_release(struct inode *inode,
struct file *filp)
{
Byte status[2], cmd_no;
sony_inuse = 0;
if (0 < sony_usage) {
sony_usage--;
}
if (sony_usage == 0) {
check_drive_status();
if (sony_audio_status != CDROM_AUDIO_PLAY) {
cmd_no = SONY535_SPIN_DOWN;
do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0);
}
#ifdef LOCK_DOORS
/* enable the eject button after umount */
cmd_no = SONY535_ENABLE_EJECT_BUTTON;
do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0);
#endif
}
return 0;
}
static struct block_device_operations cdu_fops =
{
owner: THIS_MODULE,
open: cdu_open,
release: cdu_release,
ioctl: cdu_ioctl,
check_media_change: cdu535_check_media_change,
};
static int sonycd535_block_size = CDU535_BLOCK_SIZE;
/*
* Initialize the driver.
*/
int __init
sony535_init(void)
{
struct s535_sony_drive_config drive_config;
Byte cmd_buff[3];
Byte ret_buff[2];
Byte status[2];
unsigned long snap;
int got_result = 0;
int tmp_irq;
int i;
/* Setting the base I/O address to 0 will disable it. */
if ((sony535_cd_base_io == 0xffff)||(sony535_cd_base_io == 0))
return 0;
/* Set up all the register locations */
result_reg = sony535_cd_base_io;
command_reg = sony535_cd_base_io;
data_reg = sony535_cd_base_io + 1;
read_status_reg = sony535_cd_base_io + 2;
select_unit_reg = sony535_cd_base_io + 3;
#ifndef USE_IRQ
sony535_irq_used = 0; /* polling only until this is ready... */
#endif
/* we need to poll until things get initialized */
tmp_irq = sony535_irq_used;
sony535_irq_used = 0;
#if DEBUG > 0
printk(KERN_INFO CDU535_MESSAGE_NAME ": probing base address %03X\n",
sony535_cd_base_io);
#endif
if (check_region(sony535_cd_base_io,4)) {
printk(CDU535_MESSAGE_NAME ": my base address is not free!\n");
return -EIO;
}
/* look for the CD-ROM, follows the procedure in the DOS driver */
inb(select_unit_reg);
/* wait for 40 18 Hz ticks (reverse-engineered from DOS driver) */
current->state = TASK_INTERRUPTIBLE;
schedule_timeout((HZ+17)*40/18);
inb(result_reg);
outb(0, read_status_reg); /* does a reset? */
snap = jiffies;
while (jiffies-snap < SONY_JIFFIES_TIMEOUT) {
select_unit(0);
if (inb(result_reg) != 0xff) {
got_result = 1;
break;
}
sony_sleep();
}
if (got_result && (check_drive_status() != TIME_OUT)) {
/* CD-ROM drive responded -- get the drive configuration */
cmd_buff[0] = SONY535_INQUIRY;
if (do_sony_cmd(cmd_buff, 1, status,
(Byte *)&drive_config, 28, 1) == 0) {
/* was able to get the configuration,
* set drive mode as rest of init
*/
#if DEBUG > 0
/* 0x50 == CADDY_NOT_INSERTED | NOT_SPINNING */
if ( (status[0] & 0x7f) != 0 && (status[0] & 0x7f) != 0x50 )
printk(CDU535_MESSAGE_NAME
"Inquiry command returned status = 0x%x\n", status[0]);
#endif
/* now ready to use interrupts, if available */
sony535_irq_used = tmp_irq;
#ifndef MODULE
/* This code is not in MODULEs by default, since the autoirq stuff might
* not be in the module-accessible symbol table.
*/
/* A negative sony535_irq_used will attempt an autoirq. */
if (sony535_irq_used < 0) {
autoirq_setup(0);
enable_interrupts();
outb(0, read_status_reg); /* does a reset? */
sony535_irq_used = autoirq_report(10);
disable_interrupts();
}
#endif
if (sony535_irq_used > 0) {
if (request_irq(sony535_irq_used, cdu535_interrupt,
SA_INTERRUPT, CDU535_HANDLE, NULL)) {
printk("Unable to grab IRQ%d for the " CDU535_MESSAGE_NAME
" driver; polling instead.\n", sony535_irq_used);
sony535_irq_used = 0;
}
}
cmd_buff[0] = SONY535_SET_DRIVE_MODE;
cmd_buff[1] = 0x0; /* default audio */
if (do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1) == 0) {
/* set the drive mode successful, we are set! */
sony_buffer_size = SONY535_BUFFER_SIZE;
sony_buffer_sectors = sony_buffer_size / CDU535_BLOCK_SIZE;
printk(KERN_INFO CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s",
drive_config.vendor_id,
drive_config.product_id,
drive_config.product_rev_level);
printk(" base address %03X, ", sony535_cd_base_io);
if (tmp_irq > 0)
printk("IRQ%d, ", tmp_irq);
printk("using %d byte buffer\n", sony_buffer_size);
devfs_register (NULL, CDU535_HANDLE,
DEVFS_FL_DEFAULT,
MAJOR_NR, 0,
S_IFBLK | S_IRUGO | S_IWUGO,
&cdu_fops, NULL);
if (devfs_register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) {
printk("Unable to get major %d for %s\n",
MAJOR_NR, CDU535_MESSAGE_NAME);
return -EIO;
}
blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
blksize_size[MAJOR_NR] = &sonycd535_block_size;
read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */
sony_toc = (struct s535_sony_toc *)
kmalloc(sizeof *sony_toc, GFP_KERNEL);
if (sony_toc == NULL) {
blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
return -ENOMEM;
}
last_sony_subcode = (struct s535_sony_subcode *)
kmalloc(sizeof *last_sony_subcode, GFP_KERNEL);
if (last_sony_subcode == NULL) {
blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
kfree(sony_toc);
return -ENOMEM;
}
sony_buffer = (Byte **)
kmalloc(4 * sony_buffer_sectors, GFP_KERNEL);
if (sony_buffer == NULL) {
blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
kfree(sony_toc);
kfree(last_sony_subcode);
return -ENOMEM;
}
for (i = 0; i < sony_buffer_sectors; i++) {
sony_buffer[i] =
(Byte *)kmalloc(CDU535_BLOCK_SIZE, GFP_KERNEL);
if (sony_buffer[i] == NULL) {
while (--i>=0)
kfree(sony_buffer[i]);
blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
kfree(sony_buffer);
kfree(sony_toc);
kfree(last_sony_subcode);
return -ENOMEM;
}
}
initialized = 1;
}
}
}
if (!initialized) {
printk("Did not find a " CDU535_MESSAGE_NAME " drive\n");
return -EIO;
}
request_region(sony535_cd_base_io, 4, CDU535_HANDLE);
register_disk(NULL, MKDEV(MAJOR_NR,0), 1, &cdu_fops, 0);
return 0;
}
#ifndef MODULE
/*
* accept "kernel command line" parameters
* (added by emoenke@gwdg.de)
*
* use: tell LILO:
* sonycd535=0x320
*
* the address value has to be the existing CDROM port address.
*/
static int __init
sonycd535_setup(char *strings)
{
int ints[3];
(void)get_options(strings, ARRAY_SIZE(ints), ints);
/* if IRQ change and default io base desired,
* then call with io base of 0
*/
if (ints[0] > 0)
if (ints[1] != 0)
sony535_cd_base_io = ints[1];
if (ints[0] > 1)
sony535_irq_used = ints[2];
if ((strings != NULL) && (*strings != '\0'))
printk(CDU535_MESSAGE_NAME
": Warning: Unknown interface type: %s\n", strings);
return 1;
}
__setup("sonycd535=", sonycd535_setup);
#endif /* MODULE */
void __exit
sony535_exit(void)
{
int i;
release_region(sony535_cd_base_io, 4);
for (i = 0; i < sony_buffer_sectors; i++)
kfree(sony_buffer[i]);
kfree(sony_buffer);
kfree(last_sony_subcode);
kfree(sony_toc);
devfs_unregister(devfs_find_handle(NULL, CDU535_HANDLE, 0, 0,
DEVFS_SPECIAL_BLK, 0));
if (devfs_unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL)
printk("Uh oh, couldn't unregister " CDU535_HANDLE "\n");
else
printk(KERN_INFO CDU535_HANDLE " module released\n");
}
#ifdef MODULE
module_init(sony535_init);
#endif
module_exit(sony535_exit);
MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -