📄 cdu31a.c
字号:
* how many more status bytes are coming. * * The result register can be read 10 bytes at a time, a wait for * result ready to be asserted must be done between every 10 bytes. */ if ((a & 0xf0) != 0x20) { if (b > 8) { for (i=0; i<8; i++) { *result_buffer = read_result_register(); result_buffer++; (*result_size)++; } b = b - 8; while (b > 10) { retry_count = SONY_READY_RETRIES; while ((retry_count > 0) && (!is_result_ready())) { retry_count--; } if (!is_result_ready()) { result_buffer[0] = 0x20; result_buffer[1] = SONY_TIMEOUT_OP_ERR; *result_size = 2; return; } clear_result_ready(); for (i=0; i<10; i++) { *result_buffer = read_result_register(); result_buffer++; (*result_size)++; } b = b - 10; } if (b > 0) { retry_count = SONY_READY_RETRIES; while ((retry_count > 0) && (!is_result_ready())) { retry_count--; } if (!is_result_ready()) { result_buffer[0] = 0x20; result_buffer[1] = SONY_TIMEOUT_OP_ERR; *result_size = 2; return; } } } while (b > 0) { *result_buffer = read_result_register(); result_buffer++; (*result_size)++; b--; } }}/* * Read in a 2048 byte block of data. */static voidread_data_block(unsigned char *data, unsigned char *result_buffer, unsigned int *result_size){ int i; unsigned int retry_count; for (i=0; i<2048; i++) { retry_count = jiffies + SONY_JIFFIES_TIMEOUT; while ((retry_count > jiffies) && (!is_data_requested())) { while (handle_sony_cd_attention()) ; sony_sleep(); } if (!is_data_requested()) { result_buffer[0] = 0x20; result_buffer[1] = SONY_TIMEOUT_OP_ERR; *result_size = 2; return; } *data = read_data_register(); data++; }}/* * This routine issues a read data command and gets the data. I don't * really like the way this is done (I would prefer for do_sony_cmd() to * handle it automatically) but I found that the drive returns status * when it finishes reading (not when the host has read all the data) * or after it gets an error. This means that the status can be * received at any time and should be handled immediately (at least * between every 2048 byte block) to check for errors, we can't wait * until all the data is read. * * This routine returns the total number of sectors read. It will * not return an error if it reads at least one sector successfully. */static unsigned intget_data(unsigned char *orig_data, unsigned char *params, /* 6 bytes with the MSF start address and number of sectors to read. */ unsigned int orig_data_size, unsigned char *result_buffer, unsigned int *result_size){ unsigned int cur_offset; unsigned int retry_count; int result_read; int num_retries; unsigned int num_sectors_read = 0; unsigned char *data = orig_data; unsigned int data_size = orig_data_size; cli(); while (sony_inuse) { interruptible_sleep_on(&sony_wait); if (current->signal & ~current->blocked) { result_buffer[0] = 0x20; result_buffer[1] = SONY_SIGNAL_OP_ERR; *result_size = 2; return 0; } } sony_inuse = 1; has_cd_task = current; sti(); num_retries = 0;retry_data_operation: result_buffer[0] = 0; result_buffer[1] = 0; /* * Clear any outstanding attentions and wait for the drive to * complete any pending operations. */ while (handle_sony_cd_attention()) ; retry_count = jiffies + SONY_JIFFIES_TIMEOUT; while ((retry_count > jiffies) && (is_busy())) { sony_sleep(); while (handle_sony_cd_attention()) ; } if (is_busy()) { result_buffer[0] = 0x20; result_buffer[1] = SONY_TIMEOUT_OP_ERR; *result_size = 2; } else { /* Issue the command */ clear_result_ready(); clear_param_reg(); write_params(params, 6); write_cmd(SONY_READ_CMD); /* * Read the data from the drive one 2048 byte sector at a time. Handle * any results received between sectors, if an error result is returned * terminate the operation immediately. */ cur_offset = 0; result_read = 0; while ((data_size > 0) && (result_buffer[0] == 0)) { /* Wait for the drive to tell us we have something */ retry_count = jiffies + SONY_JIFFIES_TIMEOUT; while ((retry_count > jiffies) && (!(is_result_ready() || is_data_ready()))) { while (handle_sony_cd_attention()) ; sony_sleep(); } if (!(is_result_ready() || is_data_ready())) { result_buffer[0] = 0x20; result_buffer[1] = SONY_TIMEOUT_OP_ERR; *result_size = 2; } /* Handle results first */ else if (is_result_ready()) { result_read = 1; get_result(result_buffer, result_size); } else /* Handle data next */ { /* * The drive has to be polled for status on a byte-by-byte basis * to know if the data is ready. Yuck. I really wish I could use DMA. */ clear_data_ready(); read_data_block(data, result_buffer, result_size); data += 2048; data_size -= 2048; cur_offset = cur_offset + 2048; num_sectors_read++; } } /* Make sure the result has been read */ if (!result_read) { get_result(result_buffer, result_size); } } if ( ((result_buffer[0] & 0x20) == 0x20) && (result_buffer[1] != SONY_NOT_SPIN_ERR) /* No retry when not spin */ && (num_retries < MAX_CDU31A_RETRIES)) { /* * If an error occurs, go back and only read one sector at the * given location. Hopefully the error occurred on an unused * sector after the first one. It is hard to say which sector * the error occurred on because the drive returns status before * the data transfer is finished and doesn't say which sector. */ data_size = 2048; data = orig_data; num_sectors_read = 0; size_to_buf(1, ¶ms[3]); num_retries++; /* Issue a reset on an error (the second time), othersize just delay */ if (num_retries == 2) { restart_on_error(); } else { current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + 10; schedule(); } /* Restart the operation. */ goto retry_data_operation; } has_cd_task = NULL; sony_inuse = 0; wake_up_interruptible(&sony_wait); return(num_sectors_read);}/* * Do a command that does not involve data transfer. This routine must * be re-entrant from the same task to support being called from the * data operation code when an error occurs. */static voiddo_sony_cd_cmd(unsigned char cmd, unsigned char *params, unsigned int num_params, unsigned char *result_buffer, unsigned int *result_size){ unsigned int retry_count; int num_retries; int recursive_call; cli(); if (current != has_cd_task) /* Allow recursive calls to this routine */ { while (sony_inuse) { interruptible_sleep_on(&sony_wait); if (current->signal & ~current->blocked) { result_buffer[0] = 0x20; result_buffer[1] = SONY_SIGNAL_OP_ERR; *result_size = 2; return; } } sony_inuse = 1; has_cd_task = current; recursive_call = 0; } else { recursive_call = 1; } sti(); num_retries = 0;retry_cd_operation: while (handle_sony_cd_attention()) ; retry_count = jiffies + SONY_JIFFIES_TIMEOUT; while ((retry_count > jiffies) && (is_busy())) { sony_sleep(); while (handle_sony_cd_attention()) ; } if (is_busy()) { result_buffer[0] = 0x20; result_buffer[1] = SONY_TIMEOUT_OP_ERR; *result_size = 2; } else { clear_result_ready(); clear_param_reg(); write_params(params, num_params); write_cmd(cmd); get_result(result_buffer, result_size); } if ( ((result_buffer[0] & 0x20) == 0x20) && (num_retries < MAX_CDU31A_RETRIES)) { num_retries++; current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + 10; /* Wait .1 seconds on retries */ schedule(); goto retry_cd_operation; } if (!recursive_call) { has_cd_task = NULL; sony_inuse = 0; wake_up_interruptible(&sony_wait); }}/* * Handle an attention from the drive. This will return 1 if it found one * or 0 if not (if one is found, the caller might want to call again). * * This routine counts the number of consecutive times it is called * (since this is always called from a while loop until it returns * a 0), and returns a 0 if it happens too many times. This will help * prevent a lockup. */static inthandle_sony_cd_attention(void){ unsigned char atten_code; static int num_consecutive_attentions = 0; if (is_attention()) { if (num_consecutive_attentions > CDU31A_MAX_CONSECUTIVE_ATTENTIONS) { printk("cdu31a: Too many consecutive attentions: %d\n", num_consecutive_attentions); num_consecutive_attentions = 0; return(0); } clear_attention(); atten_code = read_result_register(); switch (atten_code) { /* Someone changed the CD. Mark it as changed */ case SONY_MECH_LOADED_ATTN: sony_disc_changed = 1; sony_toc_read = 0; sony_audio_status = CDROM_AUDIO_NO_STATUS; sony_first_block = -1; sony_last_block = -1; break; case SONY_AUDIO_PLAY_DONE_ATTN: sony_audio_status = CDROM_AUDIO_COMPLETED; read_subcode(); break; case SONY_EJECT_PUSHED_ATTN: sony_audio_status = CDROM_AUDIO_INVALID; break; case SONY_LEAD_IN_ERR_ATTN: case SONY_LEAD_OUT_ERR_ATTN: case SONY_DATA_TRACK_ERR_ATTN: case SONY_AUDIO_PLAYBACK_ERR_ATTN: sony_audio_status = CDROM_AUDIO_ERROR; break; } num_consecutive_attentions++; return(1); } num_consecutive_attentions = 0; return(0);}/* Convert from an integer 0-99 to BCD */static inline unsigned intint_to_bcd(unsigned int val){ int retval; retval = (val / 10) << 4; retval = retval | val % 10; return(retval);}/* Convert from BCD to an integer from 0-99 */static unsigned intbcd_to_int(unsigned int bcd){ return((((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f));}/* * Convert a logical sector value (like the OS would want to use for * a block device) to an MSF format. */static voidlog_to_msf(unsigned int log, unsigned char *msf)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -