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

📄 cdrom.c

📁 cdrom device drive for linux.
💻 C
📖 第 1 页 / 共 5 页
字号:
	   returns with the above, but this more clearly demonstrates
	   the problem with the current interface.  Too bad this wasn't 
	   designed to use bitmasks...         -Erik 

	   Well, now we have the option CDS_MIXED: a mixed-type CD. 
	   User level programmers might feel the ioctl is not very useful.
	   					---david
	*/
	case CDROM_DISC_STATUS: {
		tracktype tracks;
		cdinfo(CD_DO_IOCTL, "entering CDROM_DISC_STATUS\n"); 
		cdrom_count_tracks(cdi, &tracks);
		if (tracks.error) 
			return(tracks.error);

		/* Policy mode on */
		if (tracks.audio > 0) {
			if (tracks.data==0 && tracks.cdi==0 && tracks.xa==0) 
				return CDS_AUDIO;
			else
				return CDS_MIXED;
		}
		if (tracks.cdi > 0) return CDS_XA_2_2;
		if (tracks.xa > 0) return CDS_XA_2_1;
		if (tracks.data > 0) return CDS_DATA_1;
		/* Policy mode off */

		cdinfo(CD_WARNING,"This disc doesn't have any tracks I recognize!\n");
		return CDS_NO_INFO;
		}

	case CDROM_CHANGER_NSLOTS: {
		cdinfo(CD_DO_IOCTL, "entering CDROM_CHANGER_NSLOTS\n"); 
		return cdi->capacity;
		}
	}

	/* use the ioctls that are implemented through the generic_packet()
	   interface. this may look at bit funny, but if -ENOTTY is
	   returned that particular ioctl is not implemented and we
	   let it go through the device specific ones. */
	if (CDROM_CAN(CDC_GENERIC_PACKET)) {
		ret = mmc_ioctl(cdi, cmd, arg);
		if (ret != -ENOTTY) {
			return ret;
		}
	}

	/* note: most of the cdinfo() calls are commented out here,
	   because they fill up the sys log when CD players poll
	   the drive. */
	switch (cmd) {
	case CDROMSUBCHNL: {
		struct cdrom_subchnl q;
		u_char requested, back;
		if (!CDROM_CAN(CDC_PLAY_AUDIO))
			return -ENOSYS;
		/* cdinfo(CD_DO_IOCTL,"entering CDROMSUBCHNL\n");*/ 
		IOCTL_IN(arg, struct cdrom_subchnl, q);
		requested = q.cdsc_format;
		if (!((requested == CDROM_MSF) ||
		      (requested == CDROM_LBA)))
			return -EINVAL;
		q.cdsc_format = CDROM_MSF;
		if ((ret=cdo->audio_ioctl(cdi, cmd, &q)))
			return ret;
		back = q.cdsc_format; /* local copy */
		sanitize_format(&q.cdsc_absaddr, &back, requested);
		sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
		IOCTL_OUT(arg, struct cdrom_subchnl, q);
		/* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ 
		return 0;
		}
	case CDROMREADTOCHDR: {
		struct cdrom_tochdr header;
		if (!CDROM_CAN(CDC_PLAY_AUDIO))
			return -ENOSYS;
		/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCHDR\n"); */ 
		IOCTL_IN(arg, struct cdrom_tochdr, header);
		if ((ret=cdo->audio_ioctl(cdi, cmd, &header)))
			return ret;
		IOCTL_OUT(arg, struct cdrom_tochdr, header);
		/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCHDR successful\n"); */ 
		return 0;
		}
	case CDROMREADTOCENTRY: {
		struct cdrom_tocentry entry;
		u_char requested_format;
		if (!CDROM_CAN(CDC_PLAY_AUDIO))
			return -ENOSYS;
		/* cdinfo(CD_DO_IOCTL, "entering CDROMREADTOCENTRY\n"); */ 
		IOCTL_IN(arg, struct cdrom_tocentry, entry);
		requested_format = entry.cdte_format;
		if (!((requested_format == CDROM_MSF) || 
			(requested_format == CDROM_LBA)))
				return -EINVAL;
		/* make interface to low-level uniform */
		entry.cdte_format = CDROM_MSF;
		if ((ret=cdo->audio_ioctl(cdi, cmd, &entry)))
			return ret;
		sanitize_format(&entry.cdte_addr,
		&entry.cdte_format, requested_format);
		IOCTL_OUT(arg, struct cdrom_tocentry, entry);
		/* cdinfo(CD_DO_IOCTL, "CDROMREADTOCENTRY successful\n"); */ 
		return 0;
		}
	case CDROMPLAYMSF: {
		struct cdrom_msf msf;
		if (!CDROM_CAN(CDC_PLAY_AUDIO))
			return -ENOSYS;
		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n"); 
		IOCTL_IN(arg, struct cdrom_msf, msf);
		return cdo->audio_ioctl(cdi, cmd, &msf);
		}
	case CDROMPLAYTRKIND: {
		struct cdrom_ti ti;
		if (!CDROM_CAN(CDC_PLAY_AUDIO))
			return -ENOSYS;
		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYTRKIND\n"); 
		IOCTL_IN(arg, struct cdrom_ti, ti);
		CHECKAUDIO;
		return cdo->audio_ioctl(cdi, cmd, &ti);
		}
	case CDROMVOLCTRL: {
		struct cdrom_volctrl volume;
		if (!CDROM_CAN(CDC_PLAY_AUDIO))
			return -ENOSYS;
		cdinfo(CD_DO_IOCTL, "entering CDROMVOLCTRL\n"); 
		IOCTL_IN(arg, struct cdrom_volctrl, volume);
		return cdo->audio_ioctl(cdi, cmd, &volume);
		}
	case CDROMVOLREAD: {
		struct cdrom_volctrl volume;
		if (!CDROM_CAN(CDC_PLAY_AUDIO))
			return -ENOSYS;
		cdinfo(CD_DO_IOCTL, "entering CDROMVOLREAD\n"); 
		if ((ret=cdo->audio_ioctl(cdi, cmd, &volume)))
			return ret;
		IOCTL_OUT(arg, struct cdrom_volctrl, volume);
		return 0;
		}
	case CDROMSTART:
	case CDROMSTOP:
	case CDROMPAUSE:
	case CDROMRESUME: {
		if (!CDROM_CAN(CDC_PLAY_AUDIO))
			return -ENOSYS;
		cdinfo(CD_DO_IOCTL, "doing audio ioctl (start/stop/pause/resume)\n"); 
		CHECKAUDIO;
		return cdo->audio_ioctl(cdi, cmd, NULL);
		}
	} /* switch */

	/* do the device specific ioctls */
	if (CDROM_CAN(CDC_IOCTLS))
		return cdo->dev_ioctl(cdi, cmd, arg);
	
	return -ENOSYS;
}

static inline
int msf_to_lba(char m, char s, char f)
{
	return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET;
}

/*
 * Required when we need to use READ_10 to issue other than 2048 block
 * reads
 */
static int cdrom_switch_blocksize(struct cdrom_device_info *cdi, int size)
{
	struct cdrom_device_ops *cdo = cdi->ops;
	struct cdrom_generic_command cgc;
	struct modesel_head mh;

	memset(&mh, 0, sizeof(mh));
	mh.block_desc_length = 0x08;
	mh.block_length_med = (size >> 8) & 0xff;
	mh.block_length_lo = size & 0xff;

	memset(&cgc, 0, sizeof(cgc));
	cgc.cmd[0] = 0x15;
	cgc.cmd[1] = 1 << 4;
	cgc.cmd[4] = 12;
	cgc.buflen = sizeof(mh);
	cgc.buffer = (char *) &mh;
	cgc.data_direction = CGC_DATA_WRITE;
	mh.block_desc_length = 0x08;
	mh.block_length_med = (size >> 8) & 0xff;
	mh.block_length_lo = size & 0xff;

	return cdo->generic_packet(cdi, &cgc);
}

static int cdrom_do_cmd(struct cdrom_device_info *cdi,
			struct cdrom_generic_command *cgc)
{
	struct request_sense *usense, sense;
	unsigned char *ubuf;
	int ret;

	if (cgc->data_direction == CGC_DATA_UNKNOWN)
		return -EINVAL;

	if (cgc->buflen < 0 || cgc->buflen >= 131072)
		return -EINVAL;

	if ((ubuf = cgc->buffer)) {
		cgc->buffer = kmalloc(cgc->buflen, GFP_KERNEL);
		if (cgc->buffer == NULL)
			return -ENOMEM;
	}

	usense = cgc->sense;
	cgc->sense = &sense;
	if (usense && !access_ok(VERIFY_WRITE, usense, sizeof(*usense)))
		return -EFAULT;

	if (cgc->data_direction == CGC_DATA_READ) {
		if (!access_ok(VERIFY_READ, ubuf, cgc->buflen))
			return -EFAULT;
	} else if (cgc->data_direction == CGC_DATA_WRITE) {
		if (copy_from_user(cgc->buffer, ubuf, cgc->buflen)) {
			kfree(cgc->buffer);
			return -EFAULT;
		}
	}

	ret = cdi->ops->generic_packet(cdi, cgc);
	__copy_to_user(usense, cgc->sense, sizeof(*usense));
	if (!ret && cgc->data_direction == CGC_DATA_READ)
		__copy_to_user(ubuf, cgc->buffer, cgc->buflen);
	kfree(cgc->buffer);
	return ret;
}

static int mmc_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
		     unsigned long arg)
{		
	struct cdrom_device_ops *cdo = cdi->ops;
	struct cdrom_generic_command cgc;
	struct request_sense sense;
	kdev_t dev = cdi->dev;
	char buffer[32];
	int ret = 0;

	memset(&cgc, 0, sizeof(cgc));

	/* build a unified command and queue it through
	   cdo->generic_packet() */
	switch (cmd) {
	case CDROMREADRAW:
	case CDROMREADMODE1:
	case CDROMREADMODE2: {
		struct cdrom_msf msf;
		int blocksize = 0, format = 0, lba;
		
		switch (cmd) {
		case CDROMREADRAW:
			blocksize = CD_FRAMESIZE_RAW;
			break;
		case CDROMREADMODE1:
			blocksize = CD_FRAMESIZE;
			format = 2;
			break;
		case CDROMREADMODE2:
			blocksize = CD_FRAMESIZE_RAW0;
			break;
		}
		IOCTL_IN(arg, struct cdrom_msf, msf);
		lba = msf_to_lba(msf.cdmsf_min0,msf.cdmsf_sec0,msf.cdmsf_frame0);
		/* FIXME: we need upper bound checking, too!! */
		if (lba < 0)
			return -EINVAL;
		cgc.buffer = (char *) kmalloc(blocksize, GFP_KERNEL);
		if (cgc.buffer == NULL)
			return -ENOMEM;
		memset(&sense, 0, sizeof(sense));
		cgc.sense = &sense;
		cgc.data_direction = CGC_DATA_READ;
		ret = cdrom_read_block(cdi, &cgc, lba, 1, format, blocksize);
		if (ret && sense.sense_key==0x05 && sense.asc==0x20 && sense.ascq==0x00) {
			/*
			 * SCSI-II devices are not required to support
			 * READ_CD, so let's try switching block size
			 */
			/* FIXME: switch back again... */
			if ((ret = cdrom_switch_blocksize(cdi, blocksize))) {
				kfree(cgc.buffer);
				return ret;
			}
			cgc.sense = NULL;
			ret = cdrom_read_cd(cdi, &cgc, lba, blocksize, 1);
			ret |= cdrom_switch_blocksize(cdi, blocksize);
		}
		if (!ret && copy_to_user((char *)arg, cgc.buffer, blocksize))
			ret = -EFAULT;
		kfree(cgc.buffer);
		return ret;
		}
	case CDROMREADAUDIO: {
		struct cdrom_read_audio ra;
		int lba, nr;

		IOCTL_IN(arg, struct cdrom_read_audio, ra);

		if (ra.addr_format == CDROM_MSF)
			lba = msf_to_lba(ra.addr.msf.minute,
					 ra.addr.msf.second,
					 ra.addr.msf.frame);
		else if (ra.addr_format == CDROM_LBA)
			lba = ra.addr.lba;
		else
			return -EINVAL;

		/* FIXME: we need upper bound checking, too!! */
		if (lba < 0 || ra.nframes <= 0)
			return -EINVAL;

		/*
		 * start with will ra.nframes size, back down if alloc fails
		 */
		nr = ra.nframes;
		do {
			cgc.buffer = kmalloc(CD_FRAMESIZE_RAW * nr, GFP_KERNEL);
			if (cgc.buffer)
				break;

			nr >>= 1;
		} while (nr);

		if (!nr)
			return -ENOMEM;

		if (!access_ok(VERIFY_WRITE, ra.buf, ra.nframes*CD_FRAMESIZE_RAW)) {
			kfree(cgc.buffer);
			return -EFAULT;
		}
		cgc.data_direction = CGC_DATA_READ;
		while (ra.nframes > 0) {
			if (nr > ra.nframes)
				nr = ra.nframes;

			ret = cdrom_read_block(cdi, &cgc, lba, nr, 1, CD_FRAMESIZE_RAW);
			if (ret)
				break;
			__copy_to_user(ra.buf, cgc.buffer, CD_FRAMESIZE_RAW*nr);
			ra.buf += CD_FRAMESIZE_RAW * nr;
			ra.nframes -= nr;
			lba += nr;
		}
		kfree(cgc.buffer);
		return ret;
		}
	case CDROMSUBCHNL: {
		struct cdrom_subchnl q;
		u_char requested, back;
		IOCTL_IN(arg, struct cdrom_subchnl, q);
		requested = q.cdsc_format;
		if (!((requested == CDROM_MSF) ||
		      (requested == CDROM_LBA)))
			return -EINVAL;
		q.cdsc_format = CDROM_MSF;
		if ((ret = cdrom_read_subchannel(cdi, &q, 0)))
			return ret;
		back = q.cdsc_format; /* local copy */
		sanitize_format(&q.cdsc_absaddr, &back, requested);
		sanitize_format(&q.cdsc_reladdr, &q.cdsc_format, requested);
		IOCTL_OUT(arg, struct cdrom_subchnl, q);
		/* cdinfo(CD_DO_IOCTL, "CDROMSUBCHNL successful\n"); */ 
		return 0;
		}
	case CDROMPLAYMSF: {
		struct cdrom_msf msf;
		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYMSF\n");
		IOCTL_IN(arg, struct cdrom_msf, msf);
		cgc.cmd[0] = GPCMD_PLAY_AUDIO_MSF;
		cgc.cmd[3] = msf.cdmsf_min0;
		cgc.cmd[4] = msf.cdmsf_sec0;
		cgc.cmd[5] = msf.cdmsf_frame0;
		cgc.cmd[6] = msf.cdmsf_min1;
		cgc.cmd[7] = msf.cdmsf_sec1;
		cgc.cmd[8] = msf.cdmsf_frame1;
		cgc.data_direction = CGC_DATA_NONE;
		return cdo->generic_packet(cdi, &cgc);
		}
	case CDROMPLAYBLK: {
		struct cdrom_blk blk;
		cdinfo(CD_DO_IOCTL, "entering CDROMPLAYBLK\n");
		IOCTL_IN(arg, struct cdrom_blk, blk);
		cgc.cmd[0] = GPCMD_PLAY_AUDIO_10;
		cgc.cmd[2] = (blk.from >> 24) & 0xff;
		cgc.cmd[3] = (blk.from >> 16) & 0xff;
		cgc.cmd[4] = (blk.from >>  8) & 0xff;
		cgc.cmd[5] = blk.from & 0xff;
		cgc.cmd[7] = (blk.len >> 8) & 0xff;
		cgc.cmd[8] = blk.len & 0xff;
		cgc.data_direction = CGC_DATA_NONE;
		return cdo->generic_packet(cdi, &cgc);
		}
	case CDROMVOLCTRL:
	case CDROMVOLREAD: {
		struct cdrom_volctrl volctrl;
		char mask[32];
		unsigned short offset;
		cdinfo(CD_DO_IOCTL, "entering CDROMVOLUME\n");

		IOCTL_IN(arg, struct cdrom_volctrl, volctrl);

		cgc.buffer = buffer;
		cgc.buflen = 24;
		if ((ret = cdrom_mode_sense(cdi, &cgc, GPMODE_AUDIO_CTL_PAGE, 0)))
		    return ret;
		
		/* some drives have longer pages, adjust and reread. */
		if (buffer[1] > c

⌨️ 快捷键说明

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