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

📄 optcd.c

📁 cdrom device drive for linux.
💻 C
📖 第 1 页 / 共 4 页
字号:
	if (status)
		return status;
	copy_from_user(&volctrl, (char *) arg, sizeof volctrl);

	msf.cdmsf_min0 = 0x10;
	msf.cdmsf_sec0 = 0x32;
	msf.cdmsf_frame0 = volctrl.channel0;
	msf.cdmsf_min1 = volctrl.channel1;
	msf.cdmsf_sec1 = volctrl.channel2;
	msf.cdmsf_frame1 = volctrl.channel3;

	status = exec_long_cmd(COMCHCTRL, &msf);
	if (status < 0) {
		DEBUG((DEBUG_VFS, "exec_long_cmd COMCHCTRL: %02x", -status));
		return -EIO;
	}
	return 0;
}


static int cdromsubchnl(unsigned long arg)
{
	int status;
	struct cdrom_subchnl subchnl;

	status = verify_area(VERIFY_WRITE, (void *) arg, sizeof subchnl);
	if (status)
		return status;
	copy_from_user(&subchnl, (void *) arg, sizeof subchnl);

	if (subchnl.cdsc_format != CDROM_LBA
	    && subchnl.cdsc_format != CDROM_MSF)
		return -EINVAL;

	status = get_q_channel(&subchnl);
	if (status < 0) {
		DEBUG((DEBUG_VFS, "get_q_channel: %02x", -status));
		return -EIO;
	}

	copy_to_user((void *) arg, &subchnl, sizeof subchnl);
	return 0;
}


static int cdromread(unsigned long arg, int blocksize, int cmd)
{
	int status;
	struct cdrom_msf msf;
	char buf[CD_FRAMESIZE_RAWER];

	status = verify_area(VERIFY_WRITE, (void *) arg, blocksize);
	if (status)
		return status;
	copy_from_user(&msf, (void *) arg, sizeof msf);

	bin2bcd(&msf);
	msf.cdmsf_min1 = 0;
	msf.cdmsf_sec1 = 0;
	msf.cdmsf_frame1 = 1;	/* read only one frame */
	status = exec_read_cmd(cmd, &msf);

	DEBUG((DEBUG_VFS, "read cmd status 0x%x", status));

	if (!sleep_flag_low(FL_DTEN, SLEEP_TIMEOUT))
		return -EIO;
	fetch_data(buf, blocksize);

	copy_to_user((void *) arg, &buf, blocksize);
	return 0;
}


static int cdromseek(unsigned long arg)
{
	int status;
	struct cdrom_msf msf;

	status = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
	if (status)
		return status;
	copy_from_user(&msf, (void *) arg, sizeof msf);

	bin2bcd(&msf);
	status = exec_seek_cmd(COMSEEK, &msf);

	DEBUG((DEBUG_VFS, "COMSEEK status 0x%x", status));

	if (status < 0)
		return -EIO;
	return 0;
}


#ifdef MULTISESSION
static int cdrommultisession(unsigned long arg)
{
	int status;
	struct cdrom_multisession ms;

	status = verify_area(VERIFY_WRITE, (void*) arg, sizeof ms);
	if (status)
		return status;
	copy_from_user(&ms, (void*) arg, sizeof ms);

	ms.addr.msf.minute = disk_info.last_session.minute;
	ms.addr.msf.second = disk_info.last_session.second;
	ms.addr.msf.frame = disk_info.last_session.frame;

	if (ms.addr_format != CDROM_LBA
	   && ms.addr_format != CDROM_MSF)
		return -EINVAL;
	if (ms.addr_format == CDROM_LBA)
		msf2lba(&ms.addr);

	ms.xa_flag = disk_info.xa;

  	copy_to_user((void*) arg, &ms,
		sizeof(struct cdrom_multisession));

#if DEBUG_MULTIS
 	if (ms.addr_format == CDROM_MSF)
               	printk(KERN_DEBUG
			"optcd: multisession xa:%d, msf:%02d:%02d.%02d\n",
			ms.xa_flag,
			ms.addr.msf.minute,
			ms.addr.msf.second,
			ms.addr.msf.frame);
	else
		printk(KERN_DEBUG
		    "optcd: multisession %d, lba:0x%08x [%02d:%02d.%02d])\n",
			ms.xa_flag,
			ms.addr.lba,
			disk_info.last_session.minute,
			disk_info.last_session.second,
			disk_info.last_session.frame);
#endif /* DEBUG_MULTIS */

	return 0;
}
#endif /* MULTISESSION */


static int cdromreset(void)
{
	if (state != S_IDLE) {
		error = 1;
		tries = 0;
	}

	toc_uptodate = 0;
	disk_changed = 1;
	opt_invalidate_buffers();
	audio_status = CDROM_AUDIO_NO_STATUS;

	if (!reset_drive())
		return -EIO;
	return 0;
}

/* VFS calls */


static int opt_ioctl(struct inode *ip, struct file *fp,
                     unsigned int cmd, unsigned long arg)
{
	int status, err, retval = 0;

	DEBUG((DEBUG_VFS, "starting opt_ioctl"));

	if (!ip)
		return -EINVAL;

	if (cmd == CDROMRESET)
		return cdromreset();

	/* is do_optcd_request or another ioctl busy? */
	if (state != S_IDLE || in_vfs)
		return -EBUSY;

	in_vfs = 1;

	status = drive_status();
	if (status < 0) {
		DEBUG((DEBUG_VFS, "drive_status: %02x", -status));
		in_vfs = 0;
		return -EIO;
	}

	if (status & ST_DOOR_OPEN)
		switch (cmd) {	/* Actions that can be taken with door open */
		case CDROMCLOSETRAY:
			/* We do this before trying to read the toc. */
			err = exec_cmd(COMCLOSE);
			if (err < 0) {
				DEBUG((DEBUG_VFS,
				       "exec_cmd COMCLOSE: %02x", -err));
				in_vfs = 0;
				return -EIO;
			}
			break;
		default:	in_vfs = 0;
				return -EBUSY;
		}

	err = update_toc();
	if (err < 0) {
		DEBUG((DEBUG_VFS, "update_toc: %02x", -err));
		in_vfs = 0;
		return -EIO;
	}

	DEBUG((DEBUG_VFS, "ioctl cmd 0x%x", cmd));

	switch (cmd) {
	case CDROMPAUSE:	retval = cdrompause(); break;
	case CDROMRESUME:	retval = cdromresume(); break;
	case CDROMPLAYMSF:	retval = cdromplaymsf(arg); break;
	case CDROMPLAYTRKIND:	retval = cdromplaytrkind(arg); break;
	case CDROMREADTOCHDR:	retval = cdromreadtochdr(arg); break;
	case CDROMREADTOCENTRY:	retval = cdromreadtocentry(arg); break;

	case CDROMSTOP:		err = exec_cmd(COMSTOP);
				if (err < 0) {
					DEBUG((DEBUG_VFS,
						"exec_cmd COMSTOP: %02x",
						-err));
					retval = -EIO;
				} else
					audio_status = CDROM_AUDIO_NO_STATUS;
				break;
	case CDROMSTART:	break;	/* This is a no-op */
	case CDROMEJECT:	err = exec_cmd(COMUNLOCK);
				if (err < 0) {
					DEBUG((DEBUG_VFS,
						"exec_cmd COMUNLOCK: %02x",
						-err));
					retval = -EIO;
					break;
				}
				err = exec_cmd(COMOPEN);
				if (err < 0) {
					DEBUG((DEBUG_VFS,
						"exec_cmd COMOPEN: %02x",
						-err));
					retval = -EIO;
				}
				break;

	case CDROMVOLCTRL:	retval = cdromvolctrl(arg); break;
	case CDROMSUBCHNL:	retval = cdromsubchnl(arg); break;

	/* The drive detects the mode and automatically delivers the
	   correct 2048 bytes, so we don't need these IOCTLs */
	case CDROMREADMODE2:	retval = -EINVAL; break;
	case CDROMREADMODE1:	retval = -EINVAL; break;

	/* Drive doesn't support reading audio */
	case CDROMREADAUDIO:	retval = -EINVAL; break;

	case CDROMEJECT_SW:	auto_eject = (char) arg;
				break;

#ifdef MULTISESSION
	case CDROMMULTISESSION:	retval = cdrommultisession(arg); break;
#endif

	case CDROM_GET_MCN:	retval = -EINVAL; break; /* not implemented */
	case CDROMVOLREAD:	retval = -EINVAL; break; /* not implemented */

	case CDROMREADRAW:
			/* this drive delivers 2340 bytes in raw mode */
			retval = cdromread(arg, CD_FRAMESIZE_RAW1, COMREADRAW);
			break;
	case CDROMREADCOOKED:
			retval = cdromread(arg, CD_FRAMESIZE, COMREAD);
			break;
	case CDROMREADALL:
			retval = cdromread(arg, CD_FRAMESIZE_RAWER, COMREADALL);
			break;

	case CDROMSEEK:		retval = cdromseek(arg); break;
	case CDROMPLAYBLK:	retval = -EINVAL; break; /* not implemented */
	case CDROMCLOSETRAY:	break;	/* The action was taken earlier */
	default:		retval = -EINVAL;
	}
	in_vfs = 0;
	return retval;
}


static int open_count = 0;

/* Open device special file; check that a disk is in. */
static int opt_open(struct inode *ip, struct file *fp)
{
	DEBUG((DEBUG_VFS, "starting opt_open"));

	if (!open_count && state == S_IDLE) {
		int status;

		toc_uptodate = 0;
		opt_invalidate_buffers();

		status = exec_cmd(COMCLOSE);	/* close door */
		if (status < 0) {
			DEBUG((DEBUG_VFS, "exec_cmd COMCLOSE: %02x", -status));
		}

		status = drive_status();
		if (status < 0) {
			DEBUG((DEBUG_VFS, "drive_status: %02x", -status));
			goto err_out;
		}
		DEBUG((DEBUG_VFS, "status: %02x", status));
		if ((status & ST_DOOR_OPEN) || (status & ST_DRVERR)) {
			printk(KERN_INFO "optcd: no disk or door open\n");
			goto err_out;
		}
		status = exec_cmd(COMLOCK);		/* Lock door */
		if (status < 0) {
			DEBUG((DEBUG_VFS, "exec_cmd COMLOCK: %02x", -status));
		}
		status = update_toc();	/* Read table of contents */
		if (status < 0)	{
			DEBUG((DEBUG_VFS, "update_toc: %02x", -status));
	 		status = exec_cmd(COMUNLOCK);	/* Unlock door */
			if (status < 0) {
				DEBUG((DEBUG_VFS,
				       "exec_cmd COMUNLOCK: %02x", -status));
			}
			goto err_out;
		}
		open_count++;
	}

	DEBUG((DEBUG_VFS, "exiting opt_open"));

	return 0;

err_out:
	return -EIO;
}


/* Release device special file; flush all blocks from the buffer cache */
static int opt_release(struct inode *ip, struct file *fp)
{
	int status;

	DEBUG((DEBUG_VFS, "executing opt_release"));
	DEBUG((DEBUG_VFS, "inode: %p, inode -> i_rdev: 0x%x, file: %p\n",
		ip, ip -> i_rdev, fp));

	if (!--open_count) {
		toc_uptodate = 0;
		opt_invalidate_buffers();
	 	status = exec_cmd(COMUNLOCK);	/* Unlock door */
		if (status < 0) {
			DEBUG((DEBUG_VFS, "exec_cmd COMUNLOCK: %02x", -status));
		}
		if (auto_eject) {
			status = exec_cmd(COMOPEN);
			DEBUG((DEBUG_VFS, "exec_cmd COMOPEN: %02x", -status));
		}
		del_timer(&delay_timer);
		del_timer(&req_timer);
	}
	return 0;
}


/* Check if disk has been changed */
static int opt_media_change(kdev_t dev)
{
	DEBUG((DEBUG_VFS, "executing opt_media_change"));
	DEBUG((DEBUG_VFS, "dev: 0x%x; disk_changed = %d\n", dev, disk_changed));

	if (disk_changed) {
		disk_changed = 0;
		return 1;
	}
	return 0;
}

/* Driver initialisation */


/* Returns 1 if a drive is detected with a version string
   starting with "DOLPHIN". Otherwise 0. */
static int __init version_ok(void)
{
	char devname[100];
	int count, i, ch, status;

	status = exec_cmd(COMVERSION);
	if (status < 0) {
		DEBUG((DEBUG_VFS, "exec_cmd COMVERSION: %02x", -status));
		return 0;
	}
	if ((count = get_data(1)) < 0) {
		DEBUG((DEBUG_VFS, "get_data(1): %02x", -count));
		return 0;
	}
	for (i = 0, ch = -1; count > 0; count--) {
		if ((ch = get_data(1)) < 0) {
			DEBUG((DEBUG_VFS, "get_data(1): %02x", -ch));
			break;
		}
		if (i < 99)
			devname[i++] = ch;
	}
	devname[i] = '\0';
	if (ch < 0)
		return 0;

	printk(KERN_INFO "optcd: Device %s detected\n", devname);
	return ((devname[0] == 'D')
	     && (devname[1] == 'O')
	     && (devname[2] == 'L')
	     && (devname[3] == 'P')
	     && (devname[4] == 'H')
	     && (devname[5] == 'I')
	     && (devname[6] == 'N'));
}


static struct block_device_operations opt_fops = {
	owner:			THIS_MODULE,
	open:			opt_open,
	release:		opt_release,
	ioctl:			opt_ioctl,
	check_media_change:	opt_media_change,
};

#ifndef MODULE
/* Get kernel parameter when used as a kernel driver */
static int optcd_setup(char *str)
{
	int ints[4];
	(void)get_options(str, ARRAY_SIZE(ints), ints);
	
	if (ints[0] > 0)
		optcd_port = ints[1];

 	return 1;
}

__setup("optcd=", optcd_setup);

#endif /* MODULE */

/* Test for presence of drive and initialize it. Called at boot time
   or during module initialisation. */
int __init optcd_init(void)
{
	int status;

	if (optcd_port <= 0) {
		printk(KERN_INFO
			"optcd: no Optics Storage CDROM Initialization\n");
		return -EIO;
	}
	if (check_region(optcd_port, 4)) {
		printk(KERN_ERR "optcd: conflict, I/O port 0x%x already used\n",
			optcd_port);
		return -EIO;
	}

	if (!reset_drive()) {
		printk(KERN_ERR "optcd: drive at 0x%x not ready\n", optcd_port);
		return -EIO;
	}
	if (!version_ok()) {
		printk(KERN_ERR "optcd: unknown drive detected; aborting\n");
		return -EIO;
	}
	status = exec_cmd(COMINITDOUBLE);
	if (status < 0) {
		printk(KERN_ERR "optcd: cannot init double speed mode\n");
		DEBUG((DEBUG_VFS, "exec_cmd COMINITDOUBLE: %02x", -status));
		return -EIO;
	}
	if (devfs_register_blkdev(MAJOR_NR, "optcd", &opt_fops) != 0)
	{
		printk(KERN_ERR "optcd: unable to get major %d\n", MAJOR_NR);
		return -EIO;
	}
	devfs_register (NULL, "optcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0,
			S_IFBLK | S_IRUGO | S_IWUGO, &opt_fops, NULL);
	hardsect_size[MAJOR_NR] = &hsecsize;
	blksize_size[MAJOR_NR] = &blksize;
	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
	read_ahead[MAJOR_NR] = 4;
	request_region(optcd_port, 4, "optcd");
	register_disk(NULL, MKDEV(MAJOR_NR,0), 1, &opt_fops, 0);

	printk(KERN_INFO "optcd: DOLPHIN 8000 AT CDROM at 0x%x\n", optcd_port);
	return 0;
}


void __exit optcd_exit(void)
{
	devfs_unregister(devfs_find_handle(NULL, "optcd", 0, 0,
					   DEVFS_SPECIAL_BLK, 0));
	if (devfs_unregister_blkdev(MAJOR_NR, "optcd") == -EINVAL) {
		printk(KERN_ERR "optcd: what's that: can't unregister\n");
		return;
	}
	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
	release_region(optcd_port, 4);
	printk(KERN_INFO "optcd: module released.\n");
}

#ifdef MODULE
module_init(optcd_init);
#endif
module_exit(optcd_exit);


MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -