📄 cm206.c
字号:
del_timer(&cd->timer); wake_up_interruptible(&cd->uart); } } /* data ready in fifo? */ else if (cd->intr_ds & ds_data_ready) { if (cd->background) ++cd->adapter_last; if (waitqueue_active(&cd->data) && (cd->wait_back || !cd->background)) { del_timer(&cd->timer); wake_up_interruptible(&cd->data); } stats(data_ready); } /* ready to issue a write command? */ else if (cd->command && cd->intr_ls & ls_transmitter_buffer_empty) { outw(dc_normal | (inw(r_data_status) & 0x7f), r_data_control); outw(cd->command, r_uart_transmit); cd->command=0; if (!cd->background) wake_up_interruptible(&cd->uart); } /* now treat errors (at least, identify them for debugging) */ else if (cd->intr_ds & ds_fifo_overflow) { debug(("Fifo overflow at sectors 0x%x\n", cd->sector_first)); fool = inw(r_fifo_output_buffer); /* de-assert the interrupt */ cd->fifo_overflowed=1; /* signal one word less should be read */ stats(fifo_overflow); } else if (cd->intr_ds & ds_data_error) { debug(("Data error at sector 0x%x\n", cd->sector_first)); stats(data_error); } else if (cd->intr_ds & ds_crc_error) { debug(("CRC error at sector 0x%x\n", cd->sector_first)); stats(crc_error); } else if (cd->intr_ds & ds_sync_error) { debug(("Sync at sector 0x%x\n", cd->sector_first)); stats(sync_error); } else if (cd->intr_ds & ds_toc_ready) { /* do something appropriate */ } /* couldn't see why this interrupt, maybe due to init */ else { outw(dc_normal | READ_AHEAD, r_data_control); stats(lost_intr); } if (cd->background && (cd->adapter_last-cd->adapter_first == cd->max_sectors || cd->fifo_overflowed)) mark_bh(CM206_BH); /* issue a stop read command */ stats(interrupt);}/* we have put the address of the wait queue in who */void cm206_timeout(unsigned long who){ cd->timed_out = 1; debug(("Timing out\n")); wake_up_interruptible((wait_queue_head_t *)who);}/* This function returns 1 if a timeout occurred, 0 if an interrupt happened */int sleep_or_timeout(wait_queue_head_t *wait, int timeout){ cd->timed_out=0; cd->timer.data=(unsigned long) wait; cd->timer.expires = jiffies + timeout; add_timer(&cd->timer); debug(("going to sleep\n")); interruptible_sleep_on(wait); del_timer(&cd->timer); if (cd->timed_out) { cd->timed_out = 0; return 1; } else return 0;}void cm206_delay(int nr_jiffies) { DECLARE_WAIT_QUEUE_HEAD(wait); sleep_or_timeout(&wait, nr_jiffies);}void send_command(int command){ debug(("Sending 0x%x\n", command)); if (!(inw(r_line_status) & ls_transmitter_buffer_empty)) { cd->command = command; cli(); /* don't interrupt before sleep */ outw(dc_mask_sync_error | dc_no_stop_on_error | (inw(r_data_status) & 0x7f), r_data_control); /* interrupt routine sends command */ if (sleep_or_timeout(&cd->uart, UART_TIMEOUT)) { debug(("Time out on write-buffer\n")); stats(write_timeout); outw(command, r_uart_transmit); } debug(("Write commmand delayed\n")); } else outw(command, r_uart_transmit);}uch receive_byte(int timeout){ uch ret; cli(); debug(("cli\n")); ret = cd->ur[cd->ur_r]; if (cd->ur_r != cd->ur_w) { sti(); debug(("returning #%d: 0x%x\n", cd->ur_r, cd->ur[cd->ur_r])); cd->ur_r++; cd->ur_r %= UR_SIZE; return ret; } else if (sleep_or_timeout(&cd->uart, timeout)) { /* does sti() */ debug(("Time out on receive-buffer\n"));#ifdef STATISTICS if (timeout==UART_TIMEOUT) stats(receive_timeout) /* no `;'! */ else stats(dsb_timeout);#endif return 0xda; } ret = cd->ur[cd->ur_r]; debug(("slept; returning #%d: 0x%x\n", cd->ur_r, cd->ur[cd->ur_r])); cd->ur_r++; cd->ur_r %= UR_SIZE; return ret;}inline uch receive_echo(void){ return receive_byte(UART_TIMEOUT);}inline uch send_receive(int command){ send_command(command); return receive_echo();}inline uch wait_dsb(void){ return receive_byte(DSB_TIMEOUT);}int type_0_command(int command, int expect_dsb){ int e; clear_ur(); if (command != (e=send_receive(command))) { debug(("command 0x%x echoed as 0x%x\n", command, e)); stats(echo); return -1; } if (expect_dsb) { cd->dsb = wait_dsb(); /* wait for command to finish */ } return 0;}int type_1_command(int command, int bytes, uch * status) /* returns info */{ int i; if (type_0_command(command,0)) return -1; for(i=0; i<bytes; i++) status[i] = send_receive(c_gimme); return 0;} /* This function resets the adapter card. We'd better not do this too * often, because it tends to generate `lost interrupts.' */void reset_cm260(void){ outw(dc_normal | dc_initialize | READ_AHEAD, r_data_control); udelay(10); /* 3.3 mu sec minimum */ outw(dc_normal | READ_AHEAD, r_data_control);}/* fsm: frame-sec-min from linear address; one of many */void fsm(int lba, uch * fsm) { fsm[0] = lba % 75; lba /= 75; lba += 2; fsm[1] = lba % 60; fsm[2] = lba / 60;}inline int fsm2lba(uch * fsm) { return fsm[0] + 75*(fsm[1]-2 + 60*fsm[2]);}inline int f_s_m2lba(uch f, uch s, uch m){ return f + 75*(s-2 + 60*m);}int start_read(int start) { uch read_sector[4] = {c_read_data, }; int i, e; fsm(start, &read_sector[1]); clear_ur(); for (i=0; i<4; i++) if (read_sector[i] != (e=send_receive(read_sector[i]))) { debug(("read_sector: %x echoes %x\n", read_sector[i], e)); stats(echo); if (e==0xff) { /* this seems to happen often */ e = receive_echo(); debug(("Second try %x\n", e)); if (e!=read_sector[i]) return -1; } } return 0;}int stop_read(void){ int e; type_0_command(c_stop,0); if((e=receive_echo()) != 0xff) { debug(("c_stop didn't send 0xff, but 0x%x\n", e)); stats(stop_0xff); return -1; } return 0;} /* This function starts to read sectors in adapter memory, the interrupt routine should stop the read. In fact, the bottom_half routine takes care of this. Set a flag `background' in the cd struct to indicate the process. */int read_background(int start, int reading){ if (cd->background) return -1; /* can't do twice */ outw(dc_normal | BACK_AHEAD, r_data_control); if (!reading && start_read(start)) return -2; cd->adapter_first = cd->adapter_last = start; cd->background = 1; /* flag a read is going on */ return 0;}#ifdef USE_INSW#define transport_data insw#else/* this routine implements insw(,,). There was a time i had the impression that there would be any difference in error-behaviour. */void transport_data(int port, ush * dest, int count) { int i; ush * d; for (i=0, d=dest; i<count; i++, d++) *d = inw(port);}#endif#define MAX_TRIES 100int read_sector(int start){ int tries=0; if (cd->background) { cd->background=0; cd->adapter_last = -1; /* invalidate adapter memory */ stop_read(); } cd->fifo_overflowed=0; reset_cm260(); /* empty fifo etc. */ if (start_read(start)) return -1; do { if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) { debug(("Read timed out sector 0x%x\n", start)); stats(read_timeout); stop_read(); return -3; } tries++; } while (cd->intr_ds & ds_fifo_empty && tries < MAX_TRIES); if (tries>1) debug(("Took me some tries\n")) else if (tries == MAX_TRIES) debug(("MAX_TRIES tries for read sector\n")); transport_data(r_fifo_output_buffer, cd->sector, READ_AHEAD*RAW_SECTOR_SIZE/2); if (read_background(start+READ_AHEAD,1)) stats(read_background); cd->sector_first = start; cd->sector_last = start+READ_AHEAD; stats(read_restarted); return 0;}/* The function of bottom-half is to send a stop command to the drive This isn't easy because the routine is not `owned' by any process; we can't go to sleep! The variable cd->background gives the status: 0 no read pending 1 a read is pending 2 c_stop waits for write_buffer_empty 3 c_stop waits for receive_buffer_full: echo 4 c_stop waits for receive_buffer_full: 0xff*/void cm206_bh(void){ debug(("bh: %d\n", cd->background)); switch (cd->background) { case 1: stats(bh); if (!(cd->intr_ls & ls_transmitter_buffer_empty)) { cd->command = c_stop; outw(dc_mask_sync_error | dc_no_stop_on_error | (inw(r_data_status) & 0x7f), r_data_control); cd->background=2; break; /* we'd better not time-out here! */ } else outw(c_stop, r_uart_transmit); /* fall into case 2: */ case 2: /* the write has been satisfied by interrupt routine */ cd->background=3; break; case 3: if (cd->ur_r != cd->ur_w) { if (cd->ur[cd->ur_r] != c_stop) { debug(("cm206_bh: c_stop echoed 0x%x\n", cd->ur[cd->ur_r])); stats(echo); } cd->ur_r++; cd->ur_r %= UR_SIZE; } cd->background++; break; case 4: if (cd->ur_r != cd->ur_w) { if (cd->ur[cd->ur_r] != 0xff) { debug(("cm206_bh: c_stop reacted with 0x%x\n", cd->ur[cd->ur_r])); stats(stop_0xff); } cd->ur_r++; cd->ur_r %= UR_SIZE; } cd->background=0; }}/* This command clears the dsb_possible_media_change flag, so we must * retain it. */void get_drive_status(void){ uch status[2]; type_1_command(c_drive_status, 2, status); /* this might be done faster */ cd->dsb=status[0]; cd->cc=status[1]; cd->media_changed |= !!(cd->dsb & (dsb_possible_media_change | dsb_drive_not_ready | dsb_tray_not_closed));}void get_disc_status(void){ if (type_1_command(c_disc_status, 7, cd->disc_status)) { debug(("get_disc_status: error\n")); }}/* The new open. The real opening strategy is defined in cdrom.c. */static int cm206_open(struct cdrom_device_info * cdi, int purpose) { MOD_INC_USE_COUNT; if (!cd->openfiles) { /* reset only first time */ cd->background=0; reset_cm260();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -