📄 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 intcdu_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 intcdu_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 __initsonycd535_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 __exitsony535_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 MODULEmodule_init(sony535_init);#endifmodule_exit(sony535_exit);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -