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