📄 optcd.c
字号:
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 + -