📄 sonycd535.c
字号:
* spin it up and try some more. */ for (spin_up_retry=0 ;; ++spin_up_retry) { /* This loop has been modified to support the Sony * CDU-510/515 series, thanks to Claudio Porfiri * <C.Porfiri@nisms.tei.ericsson.se>. */ /* * This part is to deal with very slow hardware. We * try at most MAX_SPINUP_RETRY times to read the same * block. A check for seek_and_read_N_blocks' result is * performed; if the result is wrong, the CDROM's engine * is restarted and the operation is tried again. */ /* * 1995-06-01: The system got problems when downloading * from Slackware CDROM, the problem seems to be: * seek_and_read_N_blocks returns BAD_STATUS and we * should wait for a while before retrying, so a new * part was added to discriminate the return value from * seek_and_read_N_blocks for the various cases. */ int readStatus = seek_and_read_N_blocks(params, read_size, status, sony_buffer, (read_size * CDU535_BLOCK_SIZE)); if (0 <= readStatus) /* Good data; common case, placed first */ break; if (readStatus == NO_ROOM || spin_up_retry == MAX_SPINUP_RETRY) { /* give up */ if (readStatus == NO_ROOM) printk(CDU535_MESSAGE_NAME " No room to read from CD\n"); else printk(CDU535_MESSAGE_NAME " Read error: 0x%.2x\n", status[0]); sony_first_block = -1; sony_last_block = -1; end_request(0); return; } if (readStatus == BAD_STATUS) { /* Sleep for a while, then retry */ current->state = TASK_INTERRUPTIBLE; schedule_timeout(RETRY_FOR_BAD_STATUS*HZ/10); }#if DEBUG > 0 printk(CDU535_MESSAGE_NAME " debug: calling spin up when reading data!\n");#endif cmd[0] = SONY535_SPIN_UP; do_sony_cmd(cmd, 1, status, NULL, 0, 0); } } /* * The data is in memory now, copy it to the buffer and advance to the * next block to read. */ copyoff = block - sony_first_block; memcpy(CURRENT->buffer, sony_buffer[copyoff / 4] + 512 * (copyoff % 4), 512); block += 1; nsect -= 1; CURRENT->buffer += 512; } end_request(1); break; case WRITE: end_request(0); break; default: panic("Unknown SONY CD cmd"); } }}/* * Read the table of contents from the drive and set sony_toc_read if * successful. */static voidsony_get_toc(void){ Byte status[2]; if (!sony_toc_read) { /* do not call check_drive_status() from here since it can call this routine */ if (request_toc_data(status, sony_toc) < 0) 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. track is * passed in bcd format */static intfind_track(int track){ int i; int num_tracks; num_tracks = bcd_to_int(sony_toc->last_track_num) - bcd_to_int(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){ Byte cmd = SONY535_REQUEST_SUB_Q_DATA; Byte status[2]; int dsc_status; if (check_drive_status() != 0) return -EIO; if ((dsc_status = do_sony_cmd(&cmd, 1, status, (Byte *) last_sony_subcode, sizeof(struct s535_sony_subcode), 1)) != 0) { printk(CDU535_MESSAGE_NAME " error 0x%.2x, %d (read_subcode)\n", status[0], dsc_status); 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; int err; /* Get attention stuff */ if (check_drive_status() != 0) return -EIO; sony_get_toc(); if (!sony_toc_read) { return -EIO; } err = verify_area(VERIFY_WRITE /* and read */ , (char *)arg, sizeof schi); if (err) return err; copy_from_user(&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; copy_to_user((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); } copy_to_user((char *)arg, &schi, sizeof schi); return 0;}/* * The big ugly ioctl handler. */static intcdu_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ unsigned int dev; Byte status[2]; Byte cmd_buff[10], params[10]; int i; int dsc_status; int err; if (!inode) { return -EINVAL; } dev = MINOR(inode->i_rdev) >> 6; if (dev != 0) { return -EINVAL; } if (check_drive_status() != 0) return -EIO; switch (cmd) { case CDROMSTART: /* Spin up the drive */ if (spin_up_drive(status) < 0) { printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTART)\n", status[0]); return -EIO; } return 0; break; case CDROMSTOP: /* Spin down the drive */ cmd_buff[0] = SONY535_HOLD; do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); /* * Spin the drive down, ignoring the error if the disk was * already not spinning. */ sony_audio_status = CDROM_AUDIO_NO_STATUS; cmd_buff[0] = SONY535_SPIN_DOWN; dsc_status = do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0); if (((dsc_status < 0) && (dsc_status != BAD_STATUS)) || ((status[0] & ~(SONY535_STATUS1_NOT_SPINNING)) != 0)) { printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTOP)\n", status[0]); return -EIO; } return 0; break; case CDROMPAUSE: /* Pause the drive */ cmd_buff[0] = SONY535_HOLD; /* CDU-31 driver uses AUDIO_STOP, not pause */ if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) { printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPAUSE)\n", status[0]); 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 */ set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status); if (sony_audio_status != CDROM_AUDIO_PAUSED) { return -EINVAL; } spin_up_drive(status); /* Start the drive at the saved position. */ cmd_buff[0] = SONY535_PLAY_AUDIO; cmd_buff[1] = 0; /* play back starting at this address */ cmd_buff[2] = cur_pos_msf[0]; cmd_buff[3] = cur_pos_msf[1]; cmd_buff[4] = cur_pos_msf[2]; cmd_buff[5] = SONY535_PLAY_AUDIO; cmd_buff[6] = 2; /* set ending address */ cmd_buff[7] = final_pos_msf[0]; cmd_buff[8] = final_pos_msf[1]; cmd_buff[9] = final_pos_msf[2]; if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) || (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) { printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMRESUME)\n", status[0]); return -EIO; } sony_audio_status = CDROM_AUDIO_PLAY; return 0; break; case CDROMPLAYMSF: /* Play starting at the given MSF address. */ err = verify_area(VERIFY_READ, (char *)arg, 6); if (err) return err; spin_up_drive(status); set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status); copy_from_user(params, (void *)arg, 6); /* The parameters are given in int, must be converted */ for (i = 0; i < 3; i++) { cmd_buff[2 + i] = int_to_bcd(params[i]); cmd_buff[7 + i] = int_to_bcd(params[i + 3]); } cmd_buff[0] = SONY535_PLAY_AUDIO; cmd_buff[1] = 0; /* play back starting at this address */ /* cmd_buff[2-4] are filled in for loop above */ cmd_buff[5] = SONY535_PLAY_AUDIO; cmd_buff[6] = 2; /* set ending address */ /* cmd_buff[7-9] are filled in for loop above */ if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) || (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) { printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYMSF)\n", status[0]); return -EIO; } /* Save the final position for pauses and resumes */ final_pos_msf[0] = cmd_buff[7]; final_pos_msf[1] = cmd_buff[8]; final_pos_msf[2] = cmd_buff[9]; sony_audio_status = CDROM_AUDIO_PLAY; return 0; break; case CDROMREADTOCHDR: /* Read the table of contents header */ { struct cdrom_tochdr *hdr; struct cdrom_tochdr loc_hdr; sony_get_toc(); if (!sony_toc_read) return -EIO; hdr = (struct cdrom_tochdr *)arg; err = verify_area(VERIFY_WRITE, hdr, sizeof *hdr); if (err) return err; loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num); loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num); copy_to_user(hdr, &loc_hdr, sizeof *hdr); } return 0; break; case CDROMREADTOCENTRY: /* Read a given table of contents entry */ { struct cdrom_tocentry *entry; struct cdrom_tocentry loc_entry; int track_idx; Byte *msf_val = NULL; sony_get_toc(); if (!sony_toc_read) { return -EIO; } entry = (struct cdrom_tocentry *)arg; err = verify_area(VERIFY_WRITE /* and read */ , entry, sizeof *entry); if (err) return err; copy_from_user(&loc_entry, entry, sizeof loc_entry); /* Lead out is handled separately since it is special. */ if (loc_entry.cdte_track == CDROM_LEADOUT) { loc_entry.cdte_adr = 0 /*sony_toc->address2 */ ; loc_entry.cdte_ctrl = sony_toc->control2; msf_val = sony_toc->lead_out_start_msf; } else { track_idx = find_track(int_to_bcd(loc_entry.cdte_track)); if (track_idx < 0) return -EINVAL; loc_entry.cdte_adr = 0 /*sony_toc->tracks[track_idx].address */ ; loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control; msf_val = sony_toc->tracks[track_idx].track_start_msf; } /* Logical buffer address or MSF format requested? */ if (loc_entry.cdte_format == CDROM_LBA) { loc_entry.cdte_addr.lba = msf_to_log(msf_val); } else if (loc_entry.cdte_format == CDROM_MSF) { loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val); loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val + 1)); loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val + 2)); } copy_to_user(entry, &loc_entry, sizeof *entry); } return 0; break; case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ { struct cdrom_ti ti; int track_idx; sony_get_toc(); if (!sony_toc_read) return -EIO; err = verify_area(VERIFY_READ, (char *)arg, sizeof ti); if (err) return err; copy_from_user(&ti, (char *)arg, sizeof ti); if ((ti.cdti_trk0 < sony_toc->first_track_num) || (sony_toc->last_track_num < ti.cdti_trk0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -