📄 cm206.c
字号:
cd->adapter_last = -1; /* invalidate adapter memory */ cd->sector_last = -1; } ++cd->openfiles; stats(open); return 0;}static void cm206_release(struct cdrom_device_info * cdi){ if (cd->openfiles==1) { if (cd->background) { cd->background=0; stop_read(); } cd->sector_last = -1; /* Make our internal buffer invalid */ FIRST_TRACK = 0; /* No valid disc status */ } --cd->openfiles; MOD_DEC_USE_COUNT;}/* Empty buffer empties $sectors$ sectors of the adapter card buffer, * and then reads a sector in kernel memory. */void empty_buffer(int sectors) { while (sectors>=0) { transport_data(r_fifo_output_buffer, cd->sector + cd->fifo_overflowed, RAW_SECTOR_SIZE/2 - cd->fifo_overflowed); --sectors; ++cd->adapter_first; /* update the current adapter sector */ cd->fifo_overflowed=0; /* reset overflow bit */ stats(sector_transferred); } cd->sector_first=cd->adapter_first-1; cd->sector_last=cd->adapter_first; /* update the buffer sector */}/* try_adapter. This function determines if the requested sector is in adapter memory, or will appear there soon. Returns 0 upon success */int try_adapter(int sector){ if (cd->adapter_first <= sector && sector < cd->adapter_last) { /* sector is in adapter memory */ empty_buffer(sector - cd->adapter_first); return 0; } else if (cd->background==1 && cd->adapter_first <= sector && sector < cd->adapter_first+cd->max_sectors) { /* a read is going on, we can wait for it */ cd->wait_back=1; while (sector >= cd->adapter_last) { if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) { debug(("Timed out during background wait: %d %d %d %d\n", sector, cd->adapter_last, cd->adapter_first, cd->background)); stats(back_read_timeout); cd->wait_back=0; return -1; } } cd->wait_back=0; empty_buffer(sector - cd->adapter_first); return 0; } else return -2;}/* This is not a very smart implementation. We could optimize for consecutive block numbers. I'm not convinced this would really bring down the processor load. */static void do_cm206_request(request_queue_t * q){ long int i, cd_sec_no; int quarter, error; uch * source, * dest; while(1) { /* repeat until all requests have been satisfied */ INIT_REQUEST; if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE) return; if (CURRENT->cmd != READ) { debug(("Non-read command %d on cdrom\n", CURRENT->cmd)); end_request(0); continue; } spin_unlock_irq(&io_request_lock); error=0; for (i=0; i<CURRENT->nr_sectors; i++) { int e1, e2; cd_sec_no = (CURRENT->sector+i)/BLOCKS_ISO; /* 4 times 512 bytes */ quarter = (CURRENT->sector+i) % BLOCKS_ISO; dest = CURRENT->buffer + i*LINUX_BLOCK_SIZE; /* is already in buffer memory? */ if (cd->sector_first <= cd_sec_no && cd_sec_no < cd->sector_last) { source = ((uch *) cd->sector) + 16 + quarter*LINUX_BLOCK_SIZE + (cd_sec_no-cd->sector_first)*RAW_SECTOR_SIZE; memcpy(dest, source, LINUX_BLOCK_SIZE); } else if (!(e1=try_adapter(cd_sec_no)) || !(e2=read_sector(cd_sec_no))) { source = ((uch *) cd->sector)+16+quarter*LINUX_BLOCK_SIZE; memcpy(dest, source, LINUX_BLOCK_SIZE); } else { error=1; debug(("cm206_request: %d %d\n", e1, e2)); } } spin_lock_irq(&io_request_lock); end_request(!error); }}/* Audio support. I've tried very hard, but the cm206 drive doesn't seem to have a get_toc (table-of-contents) function, while i'm pretty sure it must read the toc upon disc insertion. Therefore this function has been implemented through a binary search strategy. All track starts that happen to be found are stored in cd->toc[], for future use. I've spent a whole day on a bug that only shows under Workman--- I don't get it. Tried everything, nothing works. If workman asks for track# 0xaa, it'll get the wrong time back. Any other program receives the correct value. I'm stymied.*//* seek seeks to address lba. It does wait to arrive there. */void seek(int lba){ int i; uch seek_command[4]={c_seek, }; fsm(lba, &seek_command[1]); for (i=0; i<4; i++) type_0_command(seek_command[i], 0); cd->dsb = wait_dsb();}uch bcdbin(unsigned char bcd) /* stolen from mcd.c! */{ return (bcd >> 4)*10 + (bcd & 0xf);} inline uch normalize_track(uch track) { if (track<1) return 1; if (track>LAST_TRACK) return LAST_TRACK+1; return track;}/* This function does a binary search for track start. It records all * tracks seen in the process. Input $track$ must be between 1 and * #-of-tracks+1. Note that the start of the disc must be in toc[1].fsm. */int get_toc_lba(uch track){ int max=74*60*75-150, min=fsm2lba(cd->toc[1].fsm); int i, lba, l, old_lba=0; uch * q = cd->q; uch ct; /* current track */ int binary=0; const int skip = 3*60*75; /* 3 minutes */ for (i=track; i>0; i--) if (cd->toc[i].track) { min = fsm2lba(cd->toc[i].fsm); break; } lba = min + skip; do { seek(lba); type_1_command(c_read_current_q, 10, q); ct = normalize_track(q[1]); if (!cd->toc[ct].track) { l = q[9]-bcdbin(q[5]) + 75*(q[8]-bcdbin(q[4])-2 + 60*(q[7]-bcdbin(q[3]))); cd->toc[ct].track=q[1]; /* lead out still 0xaa */ fsm(l, cd->toc[ct].fsm); cd->toc[ct].q0 = q[0]; /* contains adr and ctrl info */ if (ct==track) return l; } old_lba=lba; if (binary) { if (ct < track) min = lba; else max = lba; lba = (min+max)/2; } else { if(ct < track) lba += skip; else { binary=1; max = lba; min = lba - skip; lba = (min+max)/2; } } } while (lba!=old_lba); return lba;}void update_toc_entry(uch track) { track = normalize_track(track); if (!cd->toc[track].track) get_toc_lba(track);}/* return 0 upon success */int read_toc_header(struct cdrom_tochdr * hp){ if (!FIRST_TRACK) get_disc_status(); if (hp) { int i; hp->cdth_trk0 = FIRST_TRACK; hp->cdth_trk1 = LAST_TRACK; /* fill in first track position */ for (i=0; i<3; i++) cd->toc[1].fsm[i] = cd->disc_status[3+i]; update_toc_entry(LAST_TRACK+1); /* find most entries */ return 0; } return -1;} void play_from_to_msf(struct cdrom_msf* msfp){ uch play_command[] = {c_play, msfp->cdmsf_frame0, msfp->cdmsf_sec0, msfp->cdmsf_min0, msfp->cdmsf_frame1, msfp->cdmsf_sec1, msfp->cdmsf_min1, 2, 2}; int i; for (i=0; i<9; i++) type_0_command(play_command[i], 0); for (i=0; i<3; i++) PLAY_TO.fsm[i] = play_command[i+4]; PLAY_TO.track = 0; /* say no track end */ cd->dsb = wait_dsb();} void play_from_to_track(int from, int to){ uch play_command[8] = {c_play, }; int i; if (from==0) { /* continue paused play */ for (i=0; i<3; i++) { play_command[i+1] = cd->audio_status[i+2]; play_command[i+4] = PLAY_TO.fsm[i]; } } else { update_toc_entry(from); update_toc_entry(to+1); for (i=0; i<3; i++) { play_command[i+1] = cd->toc[from].fsm[i]; PLAY_TO.fsm[i] = play_command[i+4] = cd->toc[to+1].fsm[i]; } PLAY_TO.track = to; } for (i=0; i<7; i++) type_0_command(play_command[i],0); for (i=0; i<2; i++) type_0_command(0x2, 0); /* volume */ cd->dsb = wait_dsb();}int get_current_q(struct cdrom_subchnl * qp){ int i; uch * q = cd->q; if (type_1_command(c_read_current_q, 10, q)) return 0;/* q[0] = bcdbin(q[0]); Don't think so! */ for (i=2; i<6; i++) q[i]=bcdbin(q[i]); qp->cdsc_adr = q[0] & 0xf; qp->cdsc_ctrl = q[0] >> 4; /* from mcd.c */ qp->cdsc_trk = q[1]; qp->cdsc_ind = q[2]; if (qp->cdsc_format == CDROM_MSF) { qp->cdsc_reladdr.msf.minute = q[3]; qp->cdsc_reladdr.msf.second = q[4]; qp->cdsc_reladdr.msf.frame = q[5]; qp->cdsc_absaddr.msf.minute = q[7]; qp->cdsc_absaddr.msf.second = q[8]; qp->cdsc_absaddr.msf.frame = q[9]; } else { qp->cdsc_reladdr.lba = f_s_m2lba(q[5], q[4], q[3]); qp->cdsc_absaddr.lba = f_s_m2lba(q[9], q[8], q[7]); } get_drive_status(); if (cd->dsb & dsb_play_in_progress) qp->cdsc_audiostatus = CDROM_AUDIO_PLAY ; else if (PAUSED) qp->cdsc_audiostatus = CDROM_AUDIO_PAUSED; else qp->cdsc_audiostatus = CDROM_AUDIO_NO_STATUS; return 0;}void invalidate_toc(void){ memset(cd->toc, 0, sizeof(cd->toc)); memset(cd->disc_status, 0, sizeof(cd->disc_status));}/* cdrom.c guarantees that cdte_format == CDROM_MSF */void get_toc_entry(struct cdrom_tocentry * ep){ uch track = normalize_track(ep->cdte_track); update_toc_entry(track); ep->cdte_addr.msf.frame = cd->toc[track].fsm[0]; ep->cdte_addr.msf.second = cd->toc[track].fsm[1]; ep->cdte_addr.msf.minute = cd->toc[track].fsm[2]; ep->cdte_adr = cd->toc[track].q0 & 0xf; ep->cdte_ctrl = cd->toc[track].q0 >> 4; ep->cdte_datamode=0;}/* Audio ioctl. Ioctl commands connected to audio are in such an * idiosyncratic i/o format, that we leave these untouched. Return 0 * upon success. Memory checking has been done by cdrom_ioctl(), the * calling function, as well as LBA/MSF sanitization.*/int cm206_audio_ioctl(struct cdrom_device_info * cdi, unsigned int cmd, void * arg) { switch (cmd) { case CDROMREADTOCHDR: return read_toc_header((struct cdrom_tochdr *) arg); case CDROMREADTOCENTRY: get_toc_entry((struct cdrom_tocentry *) arg); return 0; case CDROMPLAYMSF: play_from_to_msf((struct cdrom_msf *) arg); return 0; case CDROMPLAYTRKIND: /* admittedly, not particularly beautiful */ play_from_to_track(((struct cdrom_ti *)arg)->cdti_trk0, ((struct cdrom_ti *)arg)->cdti_trk1); return 0; case CDROMSTOP: PAUSED=0; if (cd->dsb & dsb_play_in_progress) return type_0_command(c_stop, 1); else return 0; case CDROMPAUSE: get_drive_status(); if (cd->dsb & dsb_play_in_progress) { type_0_command(c_stop, 1); type_1_command(c_audio_status, 5, cd->audio_status); PAUSED=1; /* say we're paused */ } return 0; case CDROMRESUME: if (PAUSED) play_from_to_track(0,0); PAUSED=0; return 0; case CDROMSTART: case CDROMVOLCTRL: return 0; case CDROMSUBCHNL: return get_current_q((struct cdrom_subchnl *)arg); default: return -EINVAL; }}/* Ioctl. These ioctls are specific to the cm206 driver. I have made some driver statistics accessible through ioctl calls. */static int cm206_ioctl(struct cdrom_device_info * cdi, unsigned int cmd, unsigned long arg){ switch (cmd) {#ifdef STATISTICS case CM206CTL_GET_STAT: if (arg >= NR_STATS) return -EINVAL; else return cd->stats[arg]; case CM206CTL_GET_LAST_STAT: if (arg >= NR_STATS) return -EINVAL; else return cd->last_stat[arg];#endif default: debug(("Unknown ioctl call 0x%x\n", cmd)); return -EINVAL; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -