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

📄 optcd.c

📁 cdrom device drive for linux.
💻 C
📖 第 1 页 / 共 4 页
字号:
	S_READ,		/* 2 */
	S_DATA,		/* 3 */
	S_STOP,		/* 4 */
	S_STOPPING	/* 5 */
};

static volatile enum state_e state = S_IDLE;
#if DEBUG_STATE
static volatile enum state_e state_old = S_STOP;
static volatile int flags_old = 0;
static volatile long state_n = 0;
#endif


/* Used as mutex to keep do_optcd_request (and other processes calling
   ioctl) out while some process is inside a VFS call.
   Reverse is accomplished by checking if state = S_IDLE upon entry
   of opt_ioctl and opt_media_change. */
static int in_vfs = 0;


static volatile int transfer_is_active = 0;
static volatile int error = 0;	/* %% do something with this?? */
static int tries;		/* ibid?? */
static int timeout = 0;

static void poll(unsigned long data);
static struct timer_list req_timer = {function: poll};


static void poll(unsigned long data)
{
	static volatile int read_count = 1;
	int flags;
	int loop_again = 1;
	int status = 0;
	int skip = 0;

	if (error) {
		printk(KERN_ERR "optcd: I/O error 0x%02x\n", error);
		opt_invalidate_buffers();
		if (!tries--) {
			printk(KERN_ERR "optcd: read block %d failed;"
				" Giving up\n", next_bn);
			if (transfer_is_active)
				loop_again = 0;
			if (CURRENT_VALID)
				end_request(0);
			tries = 5;
		}
		error = 0;
		state = S_STOP;
	}

	while (loop_again)
	{
		loop_again = 0; /* each case must flip this back to 1 if we want
		                 to come back up here */

#if DEBUG_STATE
		if (state == state_old)
			state_n++;
		else {
			state_old = state;
			if (++state_n > 1)
				printk(KERN_DEBUG "optcd: %ld times "
					"in previous state\n", state_n);
			printk(KERN_DEBUG "optcd: state %d\n", state);
			state_n = 0;
		}
#endif

		switch (state) {
		case S_IDLE:
			return;
		case S_START:
			if (in_vfs)
				break;
			if (send_cmd(COMDRVST)) {
				state = S_IDLE;
				while (CURRENT_VALID)
					end_request(0);
				return;
			}
			state = S_READ;
			timeout = READ_TIMEOUT;
			break;
		case S_READ: {
			struct cdrom_msf msf;
			if (!skip) {
				status = fetch_status();
				if (status < 0)
					break;
				if (status & ST_DSK_CHG) {
					toc_uptodate = 0;
					opt_invalidate_buffers();
				}
			}
			skip = 0;
			if ((status & ST_DOOR_OPEN) || (status & ST_DRVERR)) {
				toc_uptodate = 0;
				opt_invalidate_buffers();
				printk(KERN_WARNING "optcd: %s\n",
					(status & ST_DOOR_OPEN)
					? "door open"
					: "disk removed");
				state = S_IDLE;
				while (CURRENT_VALID)
					end_request(0);
				return;
			}
			if (!CURRENT_VALID) {
				state = S_STOP;
				loop_again = 1;
				break;
			}
			next_bn = CURRENT -> sector / 4;
			lba2msf(next_bn, &msf);
			read_count = N_BUFS;
			msf.cdmsf_frame1 = read_count; /* Not BCD! */

			DEBUG((DEBUG_REQUEST, "reading %x:%x.%x %x:%x.%x",
				msf.cdmsf_min0,
				msf.cdmsf_sec0,
				msf.cdmsf_frame0,
				msf.cdmsf_min1,
				msf.cdmsf_sec1,
				msf.cdmsf_frame1));
			DEBUG((DEBUG_REQUEST, "next_bn:%d buf_in:%d"
				" buf_out:%d buf_bn:%d",
				next_bn,
				buf_in,
				buf_out,
				buf_bn[buf_in]));

			exec_read_cmd(COMREAD, &msf);
			state = S_DATA;
			timeout = READ_TIMEOUT;
			break;
		}
		case S_DATA:
			flags = stdt_flags() & (FL_STEN|FL_DTEN);

#if DEBUG_STATE
			if (flags != flags_old) {
				flags_old = flags;
				printk(KERN_DEBUG "optcd: flags:%x\n", flags);
			}
			if (flags == FL_STEN)
				printk(KERN_DEBUG "timeout cnt: %d\n", timeout);
#endif

			switch (flags) {
			case FL_DTEN:		/* only STEN low */
				if (!tries--) {
					printk(KERN_ERR
						"optcd: read block %d failed; "
						"Giving up\n", next_bn);
					if (transfer_is_active) {
						tries = 0;
						break;
					}
					if (CURRENT_VALID)
						end_request(0);
					tries = 5;
				}
				state = S_START;
				timeout = READ_TIMEOUT;
				loop_again = 1;
			case (FL_STEN|FL_DTEN):	 /* both high */
				break;
			default:	/* DTEN low */
				tries = 5;
				if (!CURRENT_VALID && buf_in == buf_out) {
					state = S_STOP;
					loop_again = 1;
					break;
				}
				if (read_count<=0)
					printk(KERN_WARNING
						"optcd: warning - try to read"
						" 0 frames\n");
				while (read_count) {
					buf_bn[buf_in] = NOBUF;
					if (!flag_low(FL_DTEN, BUSY_TIMEOUT)) {
					/* should be no waiting here!?? */
						printk(KERN_ERR
						   "read_count:%d "
						   "CURRENT->nr_sectors:%ld "
						   "buf_in:%d\n",
							read_count,
							CURRENT->nr_sectors,
							buf_in);
						printk(KERN_ERR
							"transfer active: %x\n",
							transfer_is_active);
						read_count = 0;
						state = S_STOP;
						loop_again = 1;
						end_request(0);
						break;
					}
					fetch_data(buf+
					    CD_FRAMESIZE*buf_in,
					    CD_FRAMESIZE);
					read_count--;

					DEBUG((DEBUG_REQUEST,
						"S_DATA; ---I've read data- "
						"read_count: %d",
						read_count));
					DEBUG((DEBUG_REQUEST,
						"next_bn:%d  buf_in:%d "
						"buf_out:%d  buf_bn:%d",
						next_bn,
						buf_in,
						buf_out,
						buf_bn[buf_in]));

					buf_bn[buf_in] = next_bn++;
					if (buf_out == NOBUF)
						buf_out = buf_in;
					buf_in = buf_in + 1 ==
						N_BUFS ? 0 : buf_in + 1;
				}
				if (!transfer_is_active) {
					while (CURRENT_VALID) {
						transfer();
						if (CURRENT -> nr_sectors == 0)
							end_request(1);
						else
							break;
					}
				}

				if (CURRENT_VALID
				    && (CURRENT -> sector / 4 < next_bn ||
				    CURRENT -> sector / 4 >
				     next_bn + N_BUFS)) {
					state = S_STOP;
					loop_again = 1;
					break;
				}
				timeout = READ_TIMEOUT;
				if (read_count == 0) {
					state = S_STOP;
					loop_again = 1;
					break;
				}
			}
			break;
		case S_STOP:
			if (read_count != 0)
				printk(KERN_ERR
					"optcd: discard data=%x frames\n",
					read_count);
			flush_data();
			if (send_cmd(COMDRVST)) {
				state = S_IDLE;
				while (CURRENT_VALID)
					end_request(0);
				return;
			}
			state = S_STOPPING;
			timeout = STOP_TIMEOUT;
			break;
		case S_STOPPING:
			status = fetch_status();
			if (status < 0 && timeout)
					break;
			if ((status >= 0) && (status & ST_DSK_CHG)) {
				toc_uptodate = 0;
				opt_invalidate_buffers();
			}
			if (CURRENT_VALID) {
				if (status >= 0) {
					state = S_READ;
					loop_again = 1;
					skip = 1;
					break;
				} else {
					state = S_START;
					timeout = 1;
				}
			} else {
				state = S_IDLE;
				return;
			}
			break;
		default:
			printk(KERN_ERR "optcd: invalid state %d\n", state);
			return;
		} /* case */
	} /* while */

	if (!timeout--) {
		printk(KERN_ERR "optcd: timeout in state %d\n", state);
		state = S_STOP;
		if (exec_cmd(COMSTOP) < 0) {
			state = S_IDLE;
			while (CURRENT_VALID)
				end_request(0);
			return;
		}
	}

	mod_timer(&req_timer, jiffies + HZ/100);
}


static void do_optcd_request(request_queue_t * q)
{
	DEBUG((DEBUG_REQUEST, "do_optcd_request(%ld+%ld)",
	       CURRENT -> sector, CURRENT -> nr_sectors));

	if (disk_info.audio) {
		printk(KERN_WARNING "optcd: tried to mount an Audio CD\n");
		end_request(0);
		return;
	}

	transfer_is_active = 1;
	while (CURRENT_VALID) {
		if (CURRENT->bh) {
			if (!buffer_locked(CURRENT->bh))
				panic(DEVICE_NAME ": block not locked");
		}
		transfer();	/* First try to transfer block from buffers */
		if (CURRENT -> nr_sectors == 0) {
			end_request(1);
		} else {	/* Want to read a block not in buffer */
			buf_out = NOBUF;
			if (state == S_IDLE) {
				/* %% Should this block the request queue?? */
				if (update_toc() < 0) {
					while (CURRENT_VALID)
						end_request(0);
					break;
				}
				/* Start state machine */
				state = S_START;
				timeout = READ_TIMEOUT;
				tries = 5;
				/* %% why not start right away?? */
				mod_timer(&req_timer, jiffies + HZ/100);
			}
			break;
		}
	}
	transfer_is_active = 0;

	DEBUG((DEBUG_REQUEST, "next_bn:%d  buf_in:%d buf_out:%d  buf_bn:%d",
	       next_bn, buf_in, buf_out, buf_bn[buf_in]));
	DEBUG((DEBUG_REQUEST, "do_optcd_request ends"));
}

/* IOCTLs */


static char auto_eject = 0;

static int cdrompause(void)
{
	int status;

	if (audio_status != CDROM_AUDIO_PLAY)
		return -EINVAL;

	status = exec_cmd(COMPAUSEON);
	if (status < 0) {
		DEBUG((DEBUG_VFS, "exec_cmd COMPAUSEON: %02x", -status));
		return -EIO;
	}
	audio_status = CDROM_AUDIO_PAUSED;
	return 0;
}


static int cdromresume(void)
{
	int status;

	if (audio_status != CDROM_AUDIO_PAUSED)
		return -EINVAL;

	status = exec_cmd(COMPAUSEOFF);
	if (status < 0) {
		DEBUG((DEBUG_VFS, "exec_cmd COMPAUSEOFF: %02x", -status));
		audio_status = CDROM_AUDIO_ERROR;
		return -EIO;
	}
	audio_status = CDROM_AUDIO_PLAY;
	return 0;
}


static int cdromplaymsf(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_long_cmd(COMPLAY, &msf);
	if (status < 0) {
		DEBUG((DEBUG_VFS, "exec_long_cmd COMPLAY: %02x", -status));
		audio_status = CDROM_AUDIO_ERROR;
		return -EIO;
	}

	audio_status = CDROM_AUDIO_PLAY;
	return 0;
}


static int cdromplaytrkind(unsigned long arg)
{
	int status;
	struct cdrom_ti ti;
	struct cdrom_msf msf;

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

	if (ti.cdti_trk0 < disk_info.first
	    || ti.cdti_trk0 > disk_info.last
	    || ti.cdti_trk1 < ti.cdti_trk0)
		return -EINVAL;
	if (ti.cdti_trk1 > disk_info.last)
		ti.cdti_trk1 = disk_info.last;

	msf.cdmsf_min0 = toc[ti.cdti_trk0].cdsc_absaddr.msf.minute;
	msf.cdmsf_sec0 = toc[ti.cdti_trk0].cdsc_absaddr.msf.second;
	msf.cdmsf_frame0 = toc[ti.cdti_trk0].cdsc_absaddr.msf.frame;
	msf.cdmsf_min1 = toc[ti.cdti_trk1 + 1].cdsc_absaddr.msf.minute;
	msf.cdmsf_sec1 = toc[ti.cdti_trk1 + 1].cdsc_absaddr.msf.second;
	msf.cdmsf_frame1 = toc[ti.cdti_trk1 + 1].cdsc_absaddr.msf.frame;

	DEBUG((DEBUG_VFS, "play %02d:%02d.%02d to %02d:%02d.%02d",
		msf.cdmsf_min0,
		msf.cdmsf_sec0,
		msf.cdmsf_frame0,
		msf.cdmsf_min1,
		msf.cdmsf_sec1,
		msf.cdmsf_frame1));

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

	audio_status = CDROM_AUDIO_PLAY;
	return 0;
}


static int cdromreadtochdr(unsigned long arg)
{
	int status;
	struct cdrom_tochdr tochdr;

	status = verify_area(VERIFY_WRITE, (void *) arg, sizeof tochdr);
	if (status)
		return status;

	tochdr.cdth_trk0 = disk_info.first;
	tochdr.cdth_trk1 = disk_info.last;

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


static int cdromreadtocentry(unsigned long arg)
{
	int status;
	struct cdrom_tocentry entry;
	struct cdrom_subchnl *tocptr;

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

	if (entry.cdte_track == CDROM_LEADOUT)
		tocptr = &toc[disk_info.last + 1];
	else if (entry.cdte_track > disk_info.last
		|| entry.cdte_track < disk_info.first)
		return -EINVAL;
	else
		tocptr = &toc[entry.cdte_track];

	entry.cdte_adr = tocptr->cdsc_adr;
	entry.cdte_ctrl = tocptr->cdsc_ctrl;
	entry.cdte_addr.msf.minute = tocptr->cdsc_absaddr.msf.minute;
	entry.cdte_addr.msf.second = tocptr->cdsc_absaddr.msf.second;
	entry.cdte_addr.msf.frame = tocptr->cdsc_absaddr.msf.frame;
	/* %% What should go into entry.cdte_datamode? */

	if (entry.cdte_format == CDROM_LBA)
		msf2lba(&entry.cdte_addr);
	else if (entry.cdte_format != CDROM_MSF)
		return -EINVAL;

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


static int cdromvolctrl(unsigned long arg)
{
	int status;
	struct cdrom_volctrl volctrl;
	struct cdrom_msf msf;

	status = verify_area(VERIFY_READ, (void *) arg, sizeof volctrl);

⌨️ 快捷键说明

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