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

📄 optcd.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
}/* Address conversion routines *//* Binary to BCD (2 digits) */inline static void single_bin2bcd(u_char *p){	DEBUG((DEBUG_CONV, "bin2bcd %02d", *p));	*p = (*p % 10) | ((*p / 10) << 4);}/* Convert entire msf struct */static void bin2bcd(struct cdrom_msf *msf){	single_bin2bcd(&msf->cdmsf_min0);	single_bin2bcd(&msf->cdmsf_sec0);	single_bin2bcd(&msf->cdmsf_frame0);	single_bin2bcd(&msf->cdmsf_min1);	single_bin2bcd(&msf->cdmsf_sec1);	single_bin2bcd(&msf->cdmsf_frame1);}/* Linear block address to minute, second, frame form */#define CD_FPM	(CD_SECS * CD_FRAMES)	/* frames per minute */static void lba2msf(int lba, struct cdrom_msf *msf){	DEBUG((DEBUG_CONV, "lba2msf %d", lba));	lba += CD_MSF_OFFSET;	msf->cdmsf_min0 = lba / CD_FPM; lba %= CD_FPM;	msf->cdmsf_sec0 = lba / CD_FRAMES;	msf->cdmsf_frame0 = lba % CD_FRAMES;	msf->cdmsf_min1 = 0;	msf->cdmsf_sec1 = 0;	msf->cdmsf_frame1 = 0;	bin2bcd(msf);}/* Two BCD digits to binary */inline static u_char bcd2bin(u_char bcd){	DEBUG((DEBUG_CONV, "bcd2bin %x%02x", bcd));	return (bcd >> 4) * 10 + (bcd & 0x0f);}static void msf2lba(union cdrom_addr *addr){	addr->lba = addr->msf.minute * CD_FPM	            + addr->msf.second * CD_FRAMES	            + addr->msf.frame - CD_MSF_OFFSET;}/* Minute, second, frame address BCD to binary or to linear address,   depending on MODE */static void msf_bcd2bin(union cdrom_addr *addr){	addr->msf.minute = bcd2bin(addr->msf.minute);	addr->msf.second = bcd2bin(addr->msf.second);	addr->msf.frame = bcd2bin(addr->msf.frame);}/* High level drive commands */static int audio_status = CDROM_AUDIO_NO_STATUS;static char toc_uptodate = 0;static char disk_changed = 1;/* Get drive status, flagging completion of audio play and disk changes. */static int drive_status(void){	int status;	status = exec_cmd(COMIOCTLISTAT);	DEBUG((DEBUG_DRIVE_IF, "IOCTLISTAT: %03x", status));	if (status < 0)		return status;	if (status == 0xff)	/* No status available */		return -ERR_IF_NOSTAT;	if (((status & ST_MODE_BITS) != ST_M_AUDIO) &&		(audio_status == CDROM_AUDIO_PLAY)) {		audio_status = CDROM_AUDIO_COMPLETED;	}	if (status & ST_DSK_CHG) {		toc_uptodate = 0;		disk_changed = 1;		audio_status = CDROM_AUDIO_NO_STATUS;	}	return status;}/* Read the current Q-channel info. Also used for reading the   table of contents. qp->cdsc_format must be set on entry to   indicate the desired address format */static int get_q_channel(struct cdrom_subchnl *qp){	int status, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10;	status = drive_status();	if (status < 0)		return status;	qp->cdsc_audiostatus = audio_status;	status = exec_cmd(COMSUBQ);	if (status < 0)		return status;	d1 = get_data(0);	if (d1 < 0)		return d1;	qp->cdsc_adr = d1;	qp->cdsc_ctrl = d1 >> 4;	d2 = get_data(0);	if (d2 < 0)		return d2;	qp->cdsc_trk = bcd2bin(d2);	d3 = get_data(0);	if (d3 < 0)		return d3;	qp->cdsc_ind = bcd2bin(d3);	d4 = get_data(0);	if (d4 < 0)		return d4;	qp->cdsc_reladdr.msf.minute = d4;	d5 = get_data(0);	if (d5 < 0)		return d5;	qp->cdsc_reladdr.msf.second = d5;	d6 = get_data(0);	if (d6 < 0)		return d6;	qp->cdsc_reladdr.msf.frame = d6;	d7 = get_data(0);	if (d7 < 0)		return d7;	/* byte not used */	d8 = get_data(0);	if (d8 < 0)		return d8;	qp->cdsc_absaddr.msf.minute = d8;	d9 = get_data(0);	if (d9 < 0)		return d9;	qp->cdsc_absaddr.msf.second = d9;	d10 = get_data(0);	if (d10 < 0)		return d10;	qp->cdsc_absaddr.msf.frame = d10;	DEBUG((DEBUG_TOC, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",		d1, d2, d3, d4, d5, d6, d7, d8, d9, d10));	msf_bcd2bin(&qp->cdsc_absaddr);	msf_bcd2bin(&qp->cdsc_reladdr);	if (qp->cdsc_format == CDROM_LBA) {		msf2lba(&qp->cdsc_absaddr);		msf2lba(&qp->cdsc_reladdr);	}	return 0;}/* Table of contents handling *//* Errors in table of contents */#define ERR_TOC_MISSINGINFO	0x120#define ERR_TOC_MISSINGENTRY	0x121struct cdrom_disk_info {	unsigned char		first;	unsigned char		last;	struct cdrom_msf0	disk_length;	struct cdrom_msf0	first_track;	/* Multisession info: */	unsigned char		next;	struct cdrom_msf0	next_session;	struct cdrom_msf0	last_session;	unsigned char		multi;	unsigned char		xa;	unsigned char		audio;};static struct cdrom_disk_info disk_info;#define MAX_TRACKS		111static struct cdrom_subchnl toc[MAX_TRACKS];#define QINFO_FIRSTTRACK	100 /* bcd2bin(0xa0) */#define QINFO_LASTTRACK		101 /* bcd2bin(0xa1) */#define QINFO_DISKLENGTH	102 /* bcd2bin(0xa2) */#define QINFO_NEXTSESSION	110 /* bcd2bin(0xb0) */#define I_FIRSTTRACK	0x01#define I_LASTTRACK	0x02#define I_DISKLENGTH	0x04#define I_NEXTSESSION	0x08#define I_ALL	(I_FIRSTTRACK | I_LASTTRACK | I_DISKLENGTH)#if DEBUG_TOCvoid toc_debug_info(int i){	printk(KERN_DEBUG "#%3d ctl %1x, adr %1x, track %2d index %3d"		"  %2d:%02d.%02d %2d:%02d.%02d\n",		i, toc[i].cdsc_ctrl, toc[i].cdsc_adr,		toc[i].cdsc_trk, toc[i].cdsc_ind,		toc[i].cdsc_reladdr.msf.minute,		toc[i].cdsc_reladdr.msf.second,		toc[i].cdsc_reladdr.msf.frame,		toc[i].cdsc_absaddr.msf.minute,		toc[i].cdsc_absaddr.msf.second,		toc[i].cdsc_absaddr.msf.frame);}#endifstatic int read_toc(void){	int status, limit, count;	unsigned char got_info = 0;	struct cdrom_subchnl q_info;#if DEBUG_TOC	int i;#endif	DEBUG((DEBUG_TOC, "starting read_toc"));	count = 0;	for (limit = 60; limit > 0; limit--) {		int index;		q_info.cdsc_format = CDROM_MSF;		status = get_q_channel(&q_info);		if (status < 0)			return status;		index = q_info.cdsc_ind;		if (index > 0 && index < MAX_TRACKS		    && q_info.cdsc_trk == 0 && toc[index].cdsc_ind == 0) {			toc[index] = q_info;			DEBUG((DEBUG_TOC, "got %d", index));			if (index < 100)				count++;			switch (q_info.cdsc_ind) {			case QINFO_FIRSTTRACK:				got_info |= I_FIRSTTRACK;				break;			case QINFO_LASTTRACK:				got_info |= I_LASTTRACK;				break;			case QINFO_DISKLENGTH:				got_info |= I_DISKLENGTH;				break;			case QINFO_NEXTSESSION:				got_info |= I_NEXTSESSION;				break;			}		}		if ((got_info & I_ALL) == I_ALL		    && toc[QINFO_FIRSTTRACK].cdsc_absaddr.msf.minute + count		       >= toc[QINFO_LASTTRACK].cdsc_absaddr.msf.minute + 1)			break;	}	/* Construct disk_info from TOC */	if (disk_info.first == 0) {		disk_info.first = toc[QINFO_FIRSTTRACK].cdsc_absaddr.msf.minute;		disk_info.first_track.minute =			toc[disk_info.first].cdsc_absaddr.msf.minute;		disk_info.first_track.second =			toc[disk_info.first].cdsc_absaddr.msf.second;		disk_info.first_track.frame =			toc[disk_info.first].cdsc_absaddr.msf.frame;	}	disk_info.last = toc[QINFO_LASTTRACK].cdsc_absaddr.msf.minute;	disk_info.disk_length.minute =			toc[QINFO_DISKLENGTH].cdsc_absaddr.msf.minute;	disk_info.disk_length.second =			toc[QINFO_DISKLENGTH].cdsc_absaddr.msf.second-2;	disk_info.disk_length.frame =			toc[QINFO_DISKLENGTH].cdsc_absaddr.msf.frame;	disk_info.next_session.minute =			toc[QINFO_NEXTSESSION].cdsc_reladdr.msf.minute;	disk_info.next_session.second =			toc[QINFO_NEXTSESSION].cdsc_reladdr.msf.second;	disk_info.next_session.frame =			toc[QINFO_NEXTSESSION].cdsc_reladdr.msf.frame;	disk_info.next = toc[QINFO_FIRSTTRACK].cdsc_absaddr.msf.minute;	disk_info.last_session.minute =			toc[disk_info.next].cdsc_absaddr.msf.minute;	disk_info.last_session.second =			toc[disk_info.next].cdsc_absaddr.msf.second;	disk_info.last_session.frame =			toc[disk_info.next].cdsc_absaddr.msf.frame;	toc[disk_info.last + 1].cdsc_absaddr.msf.minute =			disk_info.disk_length.minute;	toc[disk_info.last + 1].cdsc_absaddr.msf.second =			disk_info.disk_length.second;	toc[disk_info.last + 1].cdsc_absaddr.msf.frame =			disk_info.disk_length.frame;#if DEBUG_TOC	for (i = 1; i <= disk_info.last + 1; i++)		toc_debug_info(i);	toc_debug_info(QINFO_FIRSTTRACK);	toc_debug_info(QINFO_LASTTRACK);	toc_debug_info(QINFO_DISKLENGTH);	toc_debug_info(QINFO_NEXTSESSION);#endif	DEBUG((DEBUG_TOC, "exiting read_toc, got_info %x, count %d",		got_info, count));	if ((got_info & I_ALL) != I_ALL	    || toc[QINFO_FIRSTTRACK].cdsc_absaddr.msf.minute + count	       < toc[QINFO_LASTTRACK].cdsc_absaddr.msf.minute + 1)		return -ERR_TOC_MISSINGINFO;	return 0;}#ifdef MULTISESSIONstatic int get_multi_disk_info(void){	int sessions, status;	struct cdrom_msf multi_index;	for (sessions = 2; sessions < 10 /* %%for now */; sessions++) {		int count;		for (count = 100; count < MAX_TRACKS; count++) 			toc[count].cdsc_ind = 0;		multi_index.cdmsf_min0 = disk_info.next_session.minute;		multi_index.cdmsf_sec0 = disk_info.next_session.second;		multi_index.cdmsf_frame0 = disk_info.next_session.frame;		if (multi_index.cdmsf_sec0 >= 20)			multi_index.cdmsf_sec0 -= 20;		else {			multi_index.cdmsf_sec0 += 40;			multi_index.cdmsf_min0--;		}		DEBUG((DEBUG_MULTIS, "Try %d: %2d:%02d.%02d", sessions,			multi_index.cdmsf_min0,			multi_index.cdmsf_sec0,			multi_index.cdmsf_frame0));		bin2bcd(&multi_index);		multi_index.cdmsf_min1 = 0;		multi_index.cdmsf_sec1 = 0;		multi_index.cdmsf_frame1 = 1;		status = exec_read_cmd(COMREAD, &multi_index);		if (status < 0) {			DEBUG((DEBUG_TOC, "exec_read_cmd COMREAD: %02x",				-status));			break;		}		status = sleep_flag_low(FL_DTEN, MULTI_SEEK_TIMEOUT) ?				0 : -ERR_TOC_MISSINGINFO;		flush_data();		if (status < 0) {			DEBUG((DEBUG_TOC, "sleep_flag_low: %02x", -status));			break;		}		status = read_toc();		if (status < 0) {			DEBUG((DEBUG_TOC, "read_toc: %02x", -status));			break;		}		disk_info.multi = 1;	}	exec_cmd(COMSTOP);	if (status < 0)		return -EIO;	return 0;}#endif MULTISESSIONstatic int update_toc(void){	int status, count;	if (toc_uptodate)		return 0;	DEBUG((DEBUG_TOC, "starting update_toc"));	disk_info.first = 0;	for (count = 0; count < MAX_TRACKS; count++) 		toc[count].cdsc_ind = 0;	status = exec_cmd(COMLEADIN);	if (status < 0)		return -EIO;	status = read_toc();	if (status < 0) {		DEBUG((DEBUG_TOC, "read_toc: %02x", -status));		return -EIO;	}        /* Audio disk detection. Look at first track. */	disk_info.audio =		(toc[disk_info.first].cdsc_ctrl & CDROM_DATA_TRACK) ? 0 : 1;	/* XA detection */	disk_info.xa = drive_status() & ST_MODE2TRACK;	/* Multisession detection: if we want this, define MULTISESSION */	disk_info.multi = 0;#ifdef MULTISESSION 	if (disk_info.xa)		get_multi_disk_info();	/* Here disk_info.multi is set */#endif MULTISESSION	if (disk_info.multi)		printk(KERN_WARNING "optcd: Multisession support experimental, "			"see linux/Documentation/cdrom/optcd\n");	DEBUG((DEBUG_TOC, "exiting update_toc"));	toc_uptodate = 1;	return 0;}/* Request handling */#define CURRENT_VALID \	(!QUEUE_EMPTY && MAJOR(CURRENT -> rq_dev) == MAJOR_NR \	 && CURRENT -> cmd == READ && CURRENT -> sector != -1)/* Buffers for block size conversion. */#define NOBUF		-1static char buf[CD_FRAMESIZE * N_BUFS];static volatile int buf_bn[N_BUFS], next_bn;static volatile int buf_in = 0, buf_out = NOBUF;inline static void opt_invalidate_buffers(void){	int i;	DEBUG((DEBUG_BUFFERS, "executing opt_invalidate_buffers"));	for (i = 0; i < N_BUFS; i++)		buf_bn[i] = NOBUF;	buf_out = NOBUF;}/* Take care of the different block sizes between cdrom and Linux.   When Linux gets variable block sizes this will probably go away. */static void transfer(void){#if DEBUG_BUFFERS | DEBUG_REQUEST	printk(KERN_DEBUG "optcd: executing transfer\n");#endif	if (!CURRENT_VALID)		return;	while (CURRENT -> nr_sectors) {		int bn = CURRENT -> sector / 4;		int i, offs, nr_sectors;		for (i = 0; i < N_BUFS && buf_bn[i] != bn; ++i);		DEBUG((DEBUG_REQUEST, "found %d", i));		if (i >= N_BUFS) {			buf_out = NOBUF;			break;		}		offs = (i * 4 + (CURRENT -> sector & 3)) * 512;		nr_sectors = 4 - (CURRENT -> sector & 3);		if (buf_out != i) {			buf_out = i;			if (buf_bn[i] != bn) {				buf_out = NOBUF;				continue;			}		}		if (nr_sectors > CURRENT -> nr_sectors)			nr_sectors = CURRENT -> nr_sectors;		memcpy(CURRENT -> buffer, buf + offs, nr_sectors * 512);		CURRENT -> nr_sectors -= nr_sectors;		CURRENT -> sector += nr_sectors;		CURRENT -> buffer += nr_sectors * 512;	}}/* State machine for reading disk blocks */enum state_e {	S_IDLE,		/* 0 */	S_START,	/* 1 */

⌨️ 快捷键说明

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