📄 serial.c
字号:
for (i = 0; i < NR_PORTS; i++) { info = rs_table + i; if (!info->enabled || !info->uses_dma) continue; /* check for dma_descr (don't need to check for dma_eop in output dma for serial */ if (ireg & info->irq) { /* we can send a new dma bunch. make it so. */ DEBUG_LOG(info->line, "tr_interrupt %i\n", i); /* Read jiffies_usec first, * we want this time to be as late as possible */ PROCSTAT(ser_stat[info->line].tx_dma_ints++); info->last_tx_active_usec = GET_JIFFIES_USEC(); info->last_tx_active = jiffies; transmit_chars(info); } /* FIXME: here we should really check for a change in the status lines and if so call status_handle(info) */ }}/* dma input channel interrupt handler */static void rec_interrupt(int irq, void *dev_id, struct pt_regs * regs){ struct e100_serial *info; unsigned long ireg; int i;#ifdef CONFIG_SVINTO_SIM /* No receive in the simulator. Will probably be when the rest of * the serial interface works, and this piece will just be removed. */ { const char *s = "What? rec_interrupt in simulator??\n"; SIMCOUT(s,strlen(s)); } return;#endif /* find out the line that caused this irq and get it from rs_table */ ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */ for (i = 0; i < NR_PORTS; i++) { info = rs_table + i; if (!info->enabled || !info->uses_dma) continue; /* check for both dma_eop and dma_descr for the input dma channel */ if (ireg & ((info->irq << 2) | (info->irq << 3))) { /* we have received something */ receive_chars(info); } /* FIXME: here we should really check for a change in the status lines and if so call status_handle(info) */ }}static _INLINE_ intforce_eop_if_needed(struct e100_serial *info){ /* We check data_avail bit to determine if data has * arrived since last time */ unsigned char rstat = info->port[REG_STATUS]; /* error or datavail? */ if (rstat & SER_ERROR_MASK) { /* Some error has occurred. If there has been valid data, an * EOP interrupt will be made automatically. If no data, the * normal ser_interrupt should be enabled and handle it. * So do nothing! */ DEBUG_LOG(info->line, "timeout err: rstat 0x%03X\n", rstat | (info->line << 8)); return 0; } if (rstat & SER_DATA_AVAIL_MASK) { /* Ok data, no error, count it */ TIMERD(DEBUG_LOG(info->line, "timeout: rstat 0x%03X\n", rstat | (info->line << 8))); /* Read data to clear status flags */ (void)info->port[REG_DATA]; info->forced_eop = 0; START_FLUSH_FAST_TIMER(info, "magic"); return 0; } /* hit the timeout, force an EOP for the input * dma channel if we haven't already */ if (!info->forced_eop) { info->forced_eop = 1; PROCSTAT(ser_stat[info->line].timeout_flush_cnt++); DEBUG_LOG(info->line, "timeout EOP %i\n", info->line); FORCE_EOP(info); } return 1;}extern _INLINE_ voidflush_to_flip_buffer(struct e100_serial *info){ struct tty_struct *tty; struct etrax_recv_buffer *buffer; unsigned int length; unsigned long flags; if (!info->first_recv_buffer) return; save_flags(flags); cli(); if (!(tty = info->tty)) { restore_flags(flags); return; } length = tty->flip.count; while ((buffer = info->first_recv_buffer) && length < TTY_FLIPBUF_SIZE) { unsigned int count = buffer->length; if (length + count > TTY_FLIPBUF_SIZE) count = TTY_FLIPBUF_SIZE - length; memcpy(tty->flip.char_buf_ptr + length, buffer->buffer, count); memset(tty->flip.flag_buf_ptr + length, TTY_NORMAL, count); tty->flip.flag_buf_ptr[length] = buffer->error; length += count; info->recv_cnt -= count; if (count == buffer->length) { info->first_recv_buffer = buffer->next; kfree(buffer); } else { buffer->length -= count; memmove(buffer->buffer, buffer->buffer + count, buffer->length); buffer->error = TTY_NORMAL; } } if (!info->first_recv_buffer) info->last_recv_buffer = NULL; tty->flip.count = length; restore_flags(flags);#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,66) /* this includes a check for low-latency */ tty_flip_buffer_push(tty);#else queue_task_irq_off(&tty->flip.tqueue, &tq_timer);#endif}static _INLINE_ voidcheck_flush_timeout(struct e100_serial *info){ force_eop_if_needed(info); flush_to_flip_buffer(info); if (info->first_recv_buffer) START_FLUSH_FAST_TIMER(info, "flip");}#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMERstatic void flush_timeout_function(unsigned long data){ struct e100_serial *info = (struct e100_serial *)data; fast_timers[info->line].function = NULL; serial_fast_timer_expired++; TIMERD(DEBUG_LOG(info->line, "flush_timout %i ", info->line)); TIMERD(DEBUG_LOG(info->line, "num expired: %i\n", serial_fast_timer_expired)); check_flush_timeout(info);}#elif defined(CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST)static void timeout_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct e100_serial *info; int i; #ifdef CONFIG_SVINTO_SIM /* No receive in the simulator. Will probably be when the rest of * the serial interface works, and this piece will just be removed. */ { const char *s = "What? timeout_interrupt in simulator??\n"; SIMCOUT(s,strlen(s)); } return;#endif /* acknowledge the timer1 irq */ *R_TIMER_CTRL = r_timer_ctrl_shadow | IO_STATE(R_TIMER_CTRL, i1, clr); PROCSTAT(fast_timer_ints++); for (i = 0; i < NR_PORTS; i++) { info = rs_table + i; if (info->enabled && info->uses_dma) check_flush_timeout(info); }} /* timeout_interrupt */#else/* dma fifo/buffer timeout handler forces an end-of-packet for the dma input channel if no chars have been received for CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS/100 s. If CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST is configured then this handler is instead run at 15360 Hz.*/static struct timer_list flush_timer;static void timed_flush_handler(unsigned long ptr){ struct e100_serial *info; int i;#ifdef CONFIG_SVINTO_SIM return;#endif for (i = 0; i < NR_PORTS; i++) { info = rs_table + i; if (info->uses_dma) check_flush_timeout(info); } /* restart flush timer */ mod_timer(&flush_timer, jiffies + CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS);}#endif#ifdef SERIAL_HANDLE_EARLY_ERRORS/* If there is an error (ie break) when the DMA is running and * there are no bytes in the fifo the DMA is stopped and we get no * eop interrupt. Thus we have to monitor the first bytes on a DMA * transfer, and if it is without error we can turn the serial * interrupts off. *//*BREAK handling on ETRAX 100:ETRAX will generate interrupt although there is no stop bit between thecharacters.Depending on how long the break sequence is, the end of the breaksequencewill look differently:| indicates start/end of a character.B= Break character (0x00) with framing error.E= Error byte with parity error received after B characters.F= "Faked" valid byte received immediatly after B characters.V= Valid byte1. B BL ___________________________ V.._|__________|__________| |valid data |Multiple frame errors with data == 0x00 (B),the timing matches up "perfectly" so no extra ending char is detected.The RXD pin is 1 in the last interrupt, in that casewe set info->errorcode = ERRCODE_INSERT_BREAK, but we can't reallyknow if another byte will come and this really is case 2. below (e.g F=0xFF or 0xFE)If RXD pin is 0 we can expect another character (see 2. below).2. B B E or F__________________..__ V.._|__________|__________|______ | |valid data "valid" or parity errorMultiple frame errors with data == 0x00 (B),but the part of the break trigs is interpreted as a start bit (and possiblysome 0 bits followed by a number of 1 bits and a stop bit).Depending on parity settings etc. this last character can be eithera fake "valid" char (F) or have a parity error (E).If the character is valid it will be put in the buffer,we set info->errorcode = ERRCODE_SET_BREAK so the receive interruptwill set the flags so the tty will handle it,if it's an error byte it will not be put in the bufferand we set info->errorcode = ERRCODE_INSERT_BREAK.To distinguish a V byte in 1. from an F byte in 2. we keep a timestampof the last faulty char (B) and compares it with the current time:If the time elapsed time is less then 2*char_time_usec we will assumeit's a faked F char and not a Valid char and set info->errorcode = ERRCODE_SET_BREAK. Flaws in the above solution:~~~~~~~~~~~~~~~~~~~~~~~~~~~~We use the timer to distinguish a F character from a V character,if a V character is to close after the break we might make the wrong decision.TODO: The break will be delayed until an F or V character is received.*/extern void _INLINE_ handle_ser_interrupt(struct e100_serial *info){ unsigned char rstat = info->port[REG_STATUS];#ifdef SERIAL_DEBUG_INTR printk("Interrupt from serport %d\n", i);#endif/* DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */ if (rstat & SER_ERROR_MASK) { unsigned char data; info->last_rx_active_usec = GET_JIFFIES_USEC(); info->last_rx_active = jiffies; /* If we got an error, we must reset it by reading the * data_in field */ data = info->port[REG_DATA]; if (!data && (rstat & SER_FRAMING_ERR_MASK)) { /* Most likely a break, but we get interrupts over and * over again. */ if (!info->break_detected_cnt) { DEBUG_LOG(info->line, "#BRK start\n", 0); } if (rstat & SER_RXD_MASK) { /* The RX pin is high now, so the break * must be over, but.... * we can't really know if we will get another * last byte ending the break or not. * And we don't know if the byte (if any) will * have an error or look valid. */ DEBUG_LOG(info->line, "# BL BRK\n", 0); info->errorcode = ERRCODE_INSERT_BREAK; } info->break_detected_cnt++; } else { /* The error does not look like a break, but could be * the end of one */ if (info->break_detected_cnt) { DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt); info->errorcode = ERRCODE_INSERT_BREAK; } else { if (info->errorcode == ERRCODE_INSERT_BREAK) add_char_and_flag(info, '\0', TTY_BREAK); if (rstat & SER_PAR_ERR_MASK) add_char_and_flag(info, data, TTY_PARITY); else if (rstat & SER_OVERRUN_MASK) add_char_and_flag(info, data, TTY_OVERRUN); else if (rstat & SER_FRAMING_ERR_MASK) add_char_and_flag(info, data, TTY_FRAME); info->errorcode = 0; } info->break_detected_cnt = 0; DEBUG_LOG(info->line, "#iERR s d %04X\n", ((rstat & SER_ERROR_MASK) << 8) | data); } PROCSTAT(ser_stat[info->line].early_errors_cnt++); } else { /* It was a valid byte, now let the DMA do the rest */ unsigned long curr_time_u = GET_JIFFIES_USEC(); unsigned long curr_time = jiffies; if (info->break_detected_cnt) { /* Detect if this character is a new valid char or the * last char in a break sequence: If LSBits are 0 and * MSBits are high AND the time is close to the * previous interrupt we should discard it. */ long elapsed_usec = (curr_time - info->last_rx_active) * (1000000/HZ) + curr_time_u - info->last_rx_active_usec; if (elapsed_usec < 2*info->char_time_usec) { DEBUG_LOG(info->line, "FBRK %i\n", info->line); /* Report as BREAK (error) and let * receive_chars() handle it */ info->errorcode = ERRCODE_SET_BREAK; } else { DEBUG_LOG(info->line, "Not end of BRK (V)%i\n", info->line); } DEBUG_LOG(info->line, "num brk %i\n", info->break_detected_cnt); }#ifdef SERIAL_DEBUG_INTR printk("** OK, disabling ser_interupts\n");#endif e100_disable_serial_data_irq(info); info->break_detected_cnt = 0; PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++); DEBUG_LOG(info->line, "ser_int OK %d\n", info->line); } /* Restarting the DMA never hurts */ *info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart); START_FLUSH_FAST_TIMER(info, "ser_int");} /* handle_ser_interrupt */static void ser_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct e100_serial *info; int i; for (i = 0; i < NR_PORTS; i++) { info = rs_table + i;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -