📄 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 MULTISESSIONstatic 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 MULTISESSIONstatic 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")); MOD_INC_USE_COUNT; 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: MOD_DEC_USE_COUNT; 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); } MOD_DEC_USE_COUNT; 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 = { 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 MODULEmodule_init(optcd_init);#endifmodule_exit(optcd_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -