⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sonycd535.c

📁 cdrom device drive for linux.
💻 C
📖 第 1 页 / 共 4 页
字号:
				|| (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 + -