📄 cdu31a.c
字号:
{ log = log + LOG_START_OFFSET; msf[0] = int_to_bcd(log / 4500); log = log % 4500; msf[1] = int_to_bcd(log / 75); msf[2] = int_to_bcd(log % 75);}/* * Convert an MSF format to a logical sector. */static unsigned intmsf_to_log(unsigned char *msf){ unsigned int log; log = bcd_to_int(msf[2]); log += bcd_to_int(msf[1]) * 75; log += bcd_to_int(msf[0]) * 4500; log = log - LOG_START_OFFSET; return log;}/* * Take in integer size value and put it into a buffer like * the drive would want to see a number-of-sector value. */static voidsize_to_buf(unsigned int size, unsigned char *buf){ buf[0] = size / 65536; size = size % 65536; buf[1] = size / 256; buf[2] = size % 256;}/* * The OS calls this to perform a read or write operation to the drive. * Write obviously fail. Reads to a read ahead of sony_buffer_size * bytes to help speed operations. This especially helps since the OS * uses 1024 byte blocks and the drive uses 2048 byte blocks. Since most * data access on a CD is done sequentially, this saves a lot of operations. */static voiddo_cdu31a_request(void){ int block; unsigned int dev; int nsect; unsigned char params[10]; unsigned char res_reg[2]; unsigned int res_size; int copyoff; int spin_up_retry; unsigned int read_size; if (!sony_spun_up) { scd_open (NULL,NULL); } while (1) {cdu31a_request_startover: /* * The beginning here is stolen from the hard disk driver. I hope * its right. */ if (!(CURRENT) || CURRENT->dev < 0) { return; } INIT_REQUEST; dev = MINOR(CURRENT->dev); block = CURRENT->sector; nsect = CURRENT->nr_sectors; if (dev != 0) { end_request(0); goto cdu31a_request_startover; } switch(CURRENT->cmd) { case READ: /* * If the block address is invalid or the request goes beyond the end of * the media, return an error. */ if ((block / 4) >= sony_toc->lead_out_start_lba) { end_request(0); goto cdu31a_request_startover; } if (((block + nsect) / 4) >= sony_toc->lead_out_start_lba) { end_request(0); goto cdu31a_request_startover; } while (nsect > 0) { /* * If the requested sector is not currently in the read-ahead buffer, * it must be read in. */ if ((block < sony_first_block) || (block > sony_last_block)) { sony_first_block = (block / 4) * 4; log_to_msf(block/4, params); /* * If the full read-ahead would go beyond the end of the media, trim * it back to read just till the end of the media. */ if (((block / 4) + sony_buffer_sectors) >= sony_toc->lead_out_start_lba) { read_size = sony_toc->lead_out_start_lba - (block / 4); } else { read_size = sony_buffer_sectors; } size_to_buf(read_size, ¶ms[3]); /* * Read the data. If the drive was not spinning, spin it up and try * once more. I know, the goto is ugly, but I am too lazy to fix it. */ spin_up_retry = 0;try_read_again: sony_last_block = sony_first_block + (get_data(sony_buffer, params, (read_size * 2048), res_reg, &res_size) * 4) - 1; if ((res_size < 2) || (res_reg[0] != 0)) { if ((res_reg[1] == SONY_NOT_SPIN_ERR) && (!spin_up_retry)) { do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); spin_up_retry = 1; goto try_read_again; } printk("Sony CDROM Read error: 0x%2.2x\n", res_reg[1]); sony_first_block = -1; sony_last_block = -1; end_request(0); goto cdu31a_request_startover; } } /* * The data is in memory now, copy it to the buffer and advance to the * next block to read. */ copyoff = (block - sony_first_block) * 512; memcpy(CURRENT->buffer, sony_buffer+copyoff, 512); block += 1; nsect -= 1; CURRENT->buffer += 512; } end_request(1); break; case WRITE: end_request(0); break; default: panic("Unkown SONY CD cmd"); } }}/* * Read the table of contents from the drive and set sony_toc_read if * successful. */static voidsony_get_toc(void){ unsigned int res_size; if (!sony_toc_read) { do_sony_cd_cmd(SONY_REQ_TOC_DATA_CMD, NULL, 0, (unsigned char *) sony_toc, &res_size); if ((res_size < 2) || ((sony_toc->exec_status[0] & 0x20) == 0x20)) { return; } sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf); sony_toc_read = 1; }}/* * Search for a specific track in the table of contents. */static intfind_track(int track){ int i; int num_tracks; num_tracks = sony_toc->last_track_num + sony_toc->first_track_num + 1; for (i = 0; i < num_tracks; i++) { if (sony_toc->tracks[i].track == track) { return i; } } return -1;}/* * Read the subcode and put it int last_sony_subcode for future use. */static intread_subcode(void){ unsigned int res_size; do_sony_cd_cmd(SONY_REQ_SUBCODE_ADDRESS_CMD, NULL, 0, (unsigned char *) last_sony_subcode, &res_size); if ((res_size < 2) || ((last_sony_subcode->exec_status[0] & 0x20) == 0x20)) { printk("Sony CDROM error 0x%2.2x (read_subcode)\n", last_sony_subcode->exec_status[1]); return -EIO; } return 0;}/* * Get the subchannel info like the CDROMSUBCHNL command wants to see it. If * the drive is playing, the subchannel needs to be read (since it would be * changing). If the drive is paused or completed, the subcode information has * already been stored, just use that. The ioctl call wants things in decimal * (not BCD), so all the conversions are done. */static intsony_get_subchnl_info(long arg){ struct cdrom_subchnl schi; /* Get attention stuff */ while (handle_sony_cd_attention()) ; sony_get_toc(); if (!sony_toc_read) { return -EIO; } verify_area(VERIFY_READ, (char *) arg, sizeof(schi)); verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi)); memcpy_fromfs(&schi, (char *) arg, sizeof(schi)); switch (sony_audio_status) { case CDROM_AUDIO_PLAY: if (read_subcode() < 0) { return -EIO; } break; case CDROM_AUDIO_PAUSED: case CDROM_AUDIO_COMPLETED: break; case CDROM_AUDIO_NO_STATUS: schi.cdsc_audiostatus = sony_audio_status; memcpy_tofs((char *) arg, &schi, sizeof(schi)); return 0; break; case CDROM_AUDIO_INVALID: case CDROM_AUDIO_ERROR: default: return -EIO; } schi.cdsc_audiostatus = sony_audio_status; schi.cdsc_adr = last_sony_subcode->address; schi.cdsc_ctrl = last_sony_subcode->control; schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num); schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num); if (schi.cdsc_format == CDROM_MSF) { schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]); schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]); schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]); schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]); schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]); schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]); } else if (schi.cdsc_format == CDROM_LBA) { schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf); schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf); } memcpy_tofs((char *) arg, &schi, sizeof(schi)); return 0;}/* * The big ugly ioctl handler. */static intscd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ unsigned int dev; unsigned char res_reg[2]; unsigned int res_size; unsigned char params[7]; int i; if (!inode) { return -EINVAL; } dev = MINOR(inode->i_rdev) >> 6; if (dev != 0) { return -EINVAL; } switch (cmd) { case CDROMSTART: /* Spin up the drive */ do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) { printk("Sony CDROM error 0x%2.2x (CDROMSTART)\n", res_reg[1]); return -EIO; } return 0; break; case CDROMSTOP: /* Spin down the drive */ do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size); /* * Spin the drive down, ignoring the error if the disk was * already not spinning. */ sony_audio_status = CDROM_AUDIO_NO_STATUS; do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); if ( ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) && (res_reg[1] != SONY_NOT_SPIN_ERR)) { printk("Sony CDROM error 0x%2.2x (CDROMSTOP)\n", res_reg[1]); return -EIO; } return 0; break; case CDROMPAUSE: /* Pause the drive */ do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size); if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) { printk("Sony CDROM error 0x%2.2x (CDROMPAUSE)\n", res_reg[1]); return -EIO; } /* Get the current position and save it for resuming */ if (read_subcode() < 0) { return -EIO; } cur_pos_msf[0] = last_sony_subcode->abs_msf[0]; cur_pos_msf[1] = last_sony_subcode->abs_msf[1]; cur_pos_msf[2] = last_sony_subcode->abs_msf[2]; sony_audio_status = CDROM_AUDIO_PAUSED; return 0; break; case CDROMRESUME: /* Start the drive after being paused */ if (sony_audio_status != CDROM_AUDIO_PAUSED) { return -EINVAL; } do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); /* Start the drive at the saved position. */ params[1] = cur_pos_msf[0]; params[2] = cur_pos_msf[1]; params[3] = cur_pos_msf[2]; params[4] = final_pos_msf[0]; params[5] = final_pos_msf[1]; params[6] = final_pos_msf[2]; params[0] = 0x03; do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size); if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) { printk("Sony CDROM error 0x%2.2x (CDROMRESUME)\n", res_reg[1]); return -EIO; } sony_audio_status = CDROM_AUDIO_PLAY; return 0; break; case CDROMPLAYMSF: /* Play starting at the given MSF address. */ verify_area(VERIFY_READ, (char *) arg, 6); do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size); memcpy_fromfs(&(params[1]), (void *) arg, 6); /* The parameters are given in int, must be converted */ for (i=1; i<7; i++) { params[i] = int_to_bcd(params[i]); } params[0] = 0x03; do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size); if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20)) { printk("Sony CDROM error 0x%2.2x (CDROMPLAYMSF)\n", res_reg[1]); return -EIO; } /* Save the final position for pauses and resumes */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -