static void run_hw_break_test(int is_write_test) { test_complete = 0; init_simple_test(); if (is_write_test) { ts.tst = hw_write_break_test; ts.name = "hw_write_break_test"; } else { ts.tst = hw_access_break_test; ts.name = "hw_access_break_test"; } /* Activate test with initial breakpoint */ kgdb_breakpoint(); hw_break_val_access(); if (is_write_test) { if (test_complete == 2) { eprintk("kgdbts: ERROR %s broke on access\n", ts.name); hwbreaks_ok = 0; } hw_break_val_write(); } kgdb_breakpoint(); if (test_complete == 1) return; eprintk("kgdbts: ERROR %s test failed\n", ts.name); hwbreaks_ok = 0; }
/* Press key to enter kdb */ void aee_trigger_kdb(void) { int res = 0; struct wd_api *wd_api = NULL; res = get_wd_api(&wd_api); /* disable Watchdog HW, note it will not enable WDT again when kdb return */ if (res) { LOGE("aee_trigger_kdb, get wd api error\n"); } else { wd_api->wd_disable_all(); } #ifdef CONFIG_SCHED_DEBUG sysrq_sched_debug_show(); #endif LOGI("User trigger KDB\n"); mtk_set_kgdboc_var(); kgdb_breakpoint(); LOGI("Exit KDB\n"); #ifdef CONFIG_LOCAL_WDT /* enable local WDT */ if (res) { LOGD("aee_trigger_kdb, get wd api error\n"); } else { wd_api->wd_restart(WD_TYPE_NOLOCK); } #endif }
void do_page_fault(struct pt_regs *regs, unsigned int vector) { /* Kernel space exception fixup check */ if (fixup_exception(regs)) return; unsigned long cr2 = read_cr2(); #ifdef CONFIG_KGDB kgdb_breakpoint(); #endif struct siginfo s; memset(&s, 0, sizeof(struct siginfo)); s.si_signo = SIGSEGV; s.si_errno = 0; s.si_addr = (void *)cr2; s.si_code = SEGV_ACCERR; // SEGV_MAPERR; int i = sigsend(current->aspace->id, ANY_ID, SIGSEGV, &s); printk(">> PAGE FAULT! Sent signal %d: CR2 is %p\n", i, s.si_addr); show_registers(regs); while (1) {} }
static void kgdb_initial_breakpoint(void) { kgdb_break_asap = 0; printk(KERN_CRIT "kgdb: Waiting for connection from remote gdb...\n"); kgdb_breakpoint(); }
void do_page_fault(struct pt_regs *regs, unsigned int vector) { /* Kernel space exception fixup check */ if (fixup_exception(regs)) return; unsigned long cr2 = read_cr2(); #ifdef CONFIG_KGDB kgdb_breakpoint(); #endif struct siginfo s; memset(&s, 0, sizeof(struct siginfo)); s.si_signo = SIGSEGV; s.si_errno = 0; s.si_addr = (void *)cr2; s.si_code = SEGV_ACCERR; // SEGV_MAPERR; int i = sigsend(current->aspace->id, ANY_ID, SIGSEGV, &s); printk(">> PAGE FAULT! Sent signal %d. CR2 is %p, cpu %d\n", i, (void *)cr2, this_cpu); show_registers(regs); /* If the fault occurred in kernel space, or the init_task, go down */ if ( (regs->rip >= PAGE_OFFSET) || (current->aspace->id == INIT_ASPACE_ID)) { local_irq_disable(); halt(); } }
int do_kgdb(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { printf("Entering KGDB mode via exception handler...\n\n"); kgdb_breakpoint(argc - 1, argv + 1); printf("\nReturned from KGDB mode\n"); return 0; }
static void run_bad_read_test(void) { init_simple_test(); ts.tst = bad_read_test; ts.name = "bad_read_test"; /* Activate test with initial breakpoint */ kgdb_breakpoint(); }
static void run_do_fork_test(void) { init_simple_test(); ts.tst = do_fork_test; ts.name = "do_fork_test"; /* Activate test with initial breakpoint */ kgdb_breakpoint(); }
static void run_sys_open_test(void) { init_simple_test(); ts.tst = sys_open_test; ts.name = "sys_open_test"; /* Activate test with initial breakpoint */ kgdb_breakpoint(); }
static int kgdb_panic_event(struct notifier_block *self, unsigned long val, void *data) { if (dbg_kdb_mode) kdb_printf("PANIC: %s\n", (char *)data); kgdb_breakpoint(); return NOTIFY_DONE; }
void breakpoint(void) { if (!initialized) { printf("breakpoint() called b4 kgdb init\n"); return; } kgdb_breakpoint(0, 0); }
static void run_singlestep_break_test(void) { init_simple_test(); ts.tst = singlestep_break_test; ts.name = "singlestep_breakpoint_test"; /* Activate test with initial breakpoint */ kgdb_breakpoint(); kgdbts_break_test(); kgdbts_break_test(); }
/* * Catch a single-step-pending thread being deleted and make sure the global * single-step state is cleared. At this point the breakpoints should have * been removed by __switch_to(). */ void arch_release_thread_stack(unsigned long *stack) { struct thread_info *ti = (void *)stack; if (kgdb_sstep_thread == ti) { kgdb_sstep_thread = NULL; /* However, we may now be running in degraded mode, with most * of the CPUs disabled until such a time as KGDB is reentered, * so force immediate reentry */ kgdb_breakpoint(); } }
static void run_nmi_sleep_test(int nmi_sleep) { unsigned long flags; init_simple_test(); ts.tst = nmi_sleep_test; ts.name = "nmi_sleep_test"; /* Activate test with initial breakpoint */ kgdb_breakpoint(); local_irq_save(flags); mdelay(nmi_sleep*1000); touch_nmi_watchdog(); local_irq_restore(flags); if (test_complete != 2) eprintk("kgdbts: ERROR nmi_test did not hit nmi\n"); kgdb_breakpoint(); if (test_complete == 1) return; eprintk("kgdbts: ERROR %s test failed\n", ts.name); }
static void run_breakpoint_test(int is_hw_breakpoint) { test_complete = 0; init_simple_test(); if (is_hw_breakpoint) { ts.tst = hw_breakpoint_test; ts.name = "hw_breakpoint_test"; } else { ts.tst = sw_breakpoint_test; ts.name = "sw_breakpoint_test"; } /* Activate test with initial breakpoint */ kgdb_breakpoint(); /* run code with the break point in it */ kgdbts_break_test(); kgdb_breakpoint(); if (test_complete) return; eprintk("kgdbts: ERROR %s test failed\n", ts.name); if (is_hw_breakpoint) hwbreaks_ok = 0; }
static int kgdb_panic_event(struct notifier_block *self, unsigned long val, void *data) { #ifdef CONFIG_KGDB_KDB if (force_panic) /* Force panic in previous KDB, so skip this time */ return NOTIFY_DONE; #endif if (!break_on_panic) return NOTIFY_DONE; if (dbg_kdb_mode) kdb_printf("PANIC: %s\n", (char *)data); kgdb_breakpoint(); return NOTIFY_DONE; }
static void sysrq_handle_dbg(int key) { if (!dbg_io_ops) { printk(KERN_CRIT "ERROR: No KGDB I/O module available\n"); return; } if (!kgdb_connected) { #ifdef CONFIG_KGDB_KDB if (!dbg_kdb_mode) printk(KERN_CRIT "KGDB or $3#33 for KDB\n"); #else printk(KERN_CRIT "Entering KGDB\n"); #endif } kgdb_breakpoint(); }
static int kgdb_panic_event(struct notifier_block *self, unsigned long val, void *data) { /* * Avoid entering the debugger if we were triggered due to a panic * We don't want to get stuck waiting for input from user in such case. * panic_timeout indicates the system should automatically * reboot on panic. */ if (panic_timeout) return NOTIFY_DONE; if (dbg_kdb_mode) kdb_printf("PANIC: %s\n", (char *)data); kgdb_breakpoint(); return NOTIFY_DONE; }
static void run_plant_and_detach_test(int is_early) { char before[BREAK_INSTR_SIZE]; char after[BREAK_INSTR_SIZE]; probe_kernel_read(before, (char *)kgdbts_break_test, BREAK_INSTR_SIZE); init_simple_test(); ts.tst = plant_and_detach_test; ts.name = "plant_and_detach_test"; /* Activate test with initial breakpoint */ if (!is_early) kgdb_breakpoint(); probe_kernel_read(after, (char *)kgdbts_break_test, BREAK_INSTR_SIZE); if (memcmp(before, after, BREAK_INSTR_SIZE)) { printk(KERN_CRIT "kgdbts: ERROR kgdb corrupted memory\n"); panic("kgdb memory corruption"); } /* complete the detach test */ if (!is_early) kgdbts_break_test(); }
/* * There are times a tasklet needs to be used vs a compiled in * break point so as to cause an exception outside a kgdb I/O module, * such as is the case with kgdboe, where calling a breakpoint in the * I/O driver itself would be fatal. */ static void kgdb_tasklet_bpt(unsigned long ing) { kgdb_breakpoint(); atomic_set(&kgdb_break_tasklet_var, 0); }
static void bfin_serial_rx_chars(struct bfin_serial_port *uart) { struct tty_struct *tty = NULL; unsigned int status, ch, flg; static struct timeval anomaly_start = { .tv_sec = 0 }; status = UART_GET_LSR(uart); UART_CLEAR_LSR(uart); ch = UART_GET_CHAR(uart); uart->port.icount.rx++; #if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) if (kgdb_connected && kgdboc_port_line == uart->port.line) if (ch == 0x3) {/* Ctrl + C */ kgdb_breakpoint(); return; } if (!uart->port.info || !uart->port.info->tty) return; #endif tty = uart->port.info->tty; if (ANOMALY_05000363) { /* The BF533 (and BF561) family of processors have a nice anomaly * where they continuously generate characters for a "single" break. * We have to basically ignore this flood until the "next" valid * character comes across. Due to the nature of the flood, it is * not possible to reliably catch bytes that are sent too quickly * after this break. So application code talking to the Blackfin * which sends a break signal must allow at least 1.5 character * times after the end of the break for things to stabilize. This * timeout was picked as it must absolutely be larger than 1 * character time +/- some percent. So 1.5 sounds good. All other * Blackfin families operate properly. Woo. */ if (anomaly_start.tv_sec) { struct timeval curr; suseconds_t usecs; if ((~ch & (~ch + 1)) & 0xff) goto known_good_char; do_gettimeofday(&curr); if (curr.tv_sec - anomaly_start.tv_sec > 1) goto known_good_char; usecs = 0; if (curr.tv_sec != anomaly_start.tv_sec) usecs += USEC_PER_SEC; usecs += curr.tv_usec - anomaly_start.tv_usec; if (usecs > UART_GET_ANOMALY_THRESHOLD(uart)) goto known_good_char; if (ch) anomaly_start.tv_sec = 0; else anomaly_start = curr; return; known_good_char: status &= ~BI; anomaly_start.tv_sec = 0; } } if (status & BI) { if (ANOMALY_05000363) if (bfin_revid() < 5) do_gettimeofday(&anomaly_start); uart->port.icount.brk++; if (uart_handle_break(&uart->port)) goto ignore_char; status &= ~(PE | FE); } if (status & PE) uart->port.icount.parity++; if (status & OE) uart->port.icount.overrun++; if (status & FE) uart->port.icount.frame++; status &= uart->port.read_status_mask; if (status & BI) flg = TTY_BREAK; else if (status & PE) flg = TTY_PARITY; else if (status & FE) flg = TTY_FRAME; else flg = TTY_NORMAL; if (uart_handle_sysrq_char(&uart->port, ch)) goto ignore_char; uart_insert_char(&uart->port, status, OE, ch, flg); ignore_char: tty_flip_buffer_push(tty); } static void bfin_serial_tx_chars(struct bfin_serial_port *uart) { struct circ_buf *xmit = &uart->port.info->xmit; /* * Check the modem control lines before * transmitting anything. */ bfin_serial_mctrl_check(uart); if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { #ifdef CONFIG_BF54x /* Clear TFI bit */ UART_PUT_LSR(uart, TFI); #endif UART_CLEAR_IER(uart, ETBEI); return; } if (uart->port.x_char) { UART_PUT_CHAR(uart, uart->port.x_char); uart->port.icount.tx++; uart->port.x_char = 0; } while ((UART_GET_LSR(uart) & THRE) && xmit->tail != xmit->head) { UART_PUT_CHAR(uart, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); uart->port.icount.tx++; SSYNC(); } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&uart->port); } static irqreturn_t bfin_serial_rx_int(int irq, void *dev_id) { struct bfin_serial_port *uart = dev_id; spin_lock(&uart->port.lock); while (UART_GET_LSR(uart) & DR) bfin_serial_rx_chars(uart); spin_unlock(&uart->port.lock); return IRQ_HANDLED; } static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id) { struct bfin_serial_port *uart = dev_id; spin_lock(&uart->port.lock); if (UART_GET_LSR(uart) & THRE) bfin_serial_tx_chars(uart); spin_unlock(&uart->port.lock); return IRQ_HANDLED; } #endif #ifdef CONFIG_SERIAL_BFIN_DMA static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart) { struct circ_buf *xmit = &uart->port.info->xmit; uart->tx_done = 0; /* * Check the modem control lines before * transmitting anything. */ bfin_serial_mctrl_check(uart); if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { uart->tx_count = 0; uart->tx_done = 1; return; } if (uart->port.x_char) { UART_PUT_CHAR(uart, uart->port.x_char); uart->port.icount.tx++; uart->port.x_char = 0; } uart->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE); if (uart->tx_count > (UART_XMIT_SIZE - xmit->tail)) uart->tx_count = UART_XMIT_SIZE - xmit->tail; blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail), (unsigned long)(xmit->buf+xmit->tail+uart->tx_count)); set_dma_config(uart->tx_dma_channel, set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP, INTR_ON_BUF, DIMENSION_LINEAR, DATA_SIZE_8, DMA_SYNC_RESTART)); set_dma_start_addr(uart->tx_dma_channel, (unsigned long)(xmit->buf+xmit->tail)); set_dma_x_count(uart->tx_dma_channel, uart->tx_count); set_dma_x_modify(uart->tx_dma_channel, 1); enable_dma(uart->tx_dma_channel); UART_SET_IER(uart, ETBEI); } static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart) { struct tty_struct *tty = uart->port.info->port.tty; int i, flg, status; status = UART_GET_LSR(uart); UART_CLEAR_LSR(uart); uart->port.icount.rx += CIRC_CNT(uart->rx_dma_buf.head, uart->rx_dma_buf.tail, UART_XMIT_SIZE); if (status & BI) { uart->port.icount.brk++; if (uart_handle_break(&uart->port)) goto dma_ignore_char; status &= ~(PE | FE); } if (status & PE) uart->port.icount.parity++; if (status & OE) uart->port.icount.overrun++; if (status & FE) uart->port.icount.frame++; status &= uart->port.read_status_mask; if (status & BI) flg = TTY_BREAK; else if (status & PE) flg = TTY_PARITY; else if (status & FE) flg = TTY_FRAME; else flg = TTY_NORMAL; for (i = uart->rx_dma_buf.tail; i != uart->rx_dma_buf.head; i++) { if (i >= UART_XMIT_SIZE) i = 0; if (!uart_handle_sysrq_char(&uart->port, uart->rx_dma_buf.buf[i])) uart_insert_char(&uart->port, status, OE, uart->rx_dma_buf.buf[i], flg); } dma_ignore_char: tty_flip_buffer_push(tty); } void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) { int x_pos, pos, flags; spin_lock_irqsave(&uart->port.lock, flags); uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel); x_pos = get_dma_curr_xcount(uart->rx_dma_channel); uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows; if (uart->rx_dma_nrows == DMA_RX_YCOUNT) uart->rx_dma_nrows = 0; x_pos = DMA_RX_XCOUNT - x_pos; if (x_pos == DMA_RX_XCOUNT) x_pos = 0; pos = uart->rx_dma_nrows * DMA_RX_XCOUNT + x_pos; if (pos != uart->rx_dma_buf.tail) { uart->rx_dma_buf.head = pos; bfin_serial_dma_rx_chars(uart); uart->rx_dma_buf.tail = uart->rx_dma_buf.head; } spin_unlock_irqrestore(&uart->port.lock, flags); mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES); }
static void bfin_serial_rx_chars(struct bfin_serial_port *uart) { struct tty_struct *tty = NULL; unsigned int status, ch, flg; static struct timeval anomaly_start = { .tv_sec = 0 }; status = UART_GET_LSR(uart); UART_CLEAR_LSR(uart); ch = UART_GET_CHAR(uart); uart->port.icount.rx++; #if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) if (kgdb_connected && kgdboc_port_line == uart->port.line && kgdboc_break_enabled) if (ch == 0x3) {/* Ctrl + C */ kgdb_breakpoint(); return; } if (!uart->port.state || !uart->port.state->port.tty) return; #endif tty = uart->port.state->port.tty; if (ANOMALY_05000363) { /* The BF533 (and BF561) family of processors have a nice anomaly * where they continuously generate characters for a "single" break. * We have to basically ignore this flood until the "next" valid * character comes across. Due to the nature of the flood, it is * not possible to reliably catch bytes that are sent too quickly * after this break. So application code talking to the Blackfin * which sends a break signal must allow at least 1.5 character * times after the end of the break for things to stabilize. This * timeout was picked as it must absolutely be larger than 1 * character time +/- some percent. So 1.5 sounds good. All other * Blackfin families operate properly. Woo. */ if (anomaly_start.tv_sec) { struct timeval curr; suseconds_t usecs; if ((~ch & (~ch + 1)) & 0xff) goto known_good_char; do_gettimeofday(&curr); if (curr.tv_sec - anomaly_start.tv_sec > 1) goto known_good_char; usecs = 0; if (curr.tv_sec != anomaly_start.tv_sec) usecs += USEC_PER_SEC; usecs += curr.tv_usec - anomaly_start.tv_usec; if (usecs > UART_GET_ANOMALY_THRESHOLD(uart)) goto known_good_char; if (ch) anomaly_start.tv_sec = 0; else anomaly_start = curr; return; known_good_char: status &= ~BI; anomaly_start.tv_sec = 0; } } if (status & BI) { if (ANOMALY_05000363) if (bfin_revid() < 5) do_gettimeofday(&anomaly_start); uart->port.icount.brk++; if (uart_handle_break(&uart->port)) goto ignore_char; status &= ~(PE | FE); } if (status & PE) uart->port.icount.parity++; if (status & OE) uart->port.icount.overrun++; if (status & FE) uart->port.icount.frame++; status &= uart->port.read_status_mask; if (status & BI) flg = TTY_BREAK; else if (status & PE) flg = TTY_PARITY; else if (status & FE) flg = TTY_FRAME; else flg = TTY_NORMAL; if (uart_handle_sysrq_char(&uart->port, ch)) goto ignore_char; uart_insert_char(&uart->port, status, OE, ch, flg); ignore_char: tty_flip_buffer_push(tty); } static void bfin_serial_tx_chars(struct bfin_serial_port *uart) { struct circ_buf *xmit = &uart->port.state->xmit; if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { #ifdef CONFIG_BF54x /* Clear TFI bit */ UART_PUT_LSR(uart, TFI); #endif /* Anomaly notes: * 05000215 - we always clear ETBEI within last UART TX * interrupt to end a string. It is always set * when start a new tx. */ UART_CLEAR_IER(uart, ETBEI); return; } if (uart->port.x_char) { UART_PUT_CHAR(uart, uart->port.x_char); uart->port.icount.tx++; uart->port.x_char = 0; } while ((UART_GET_LSR(uart) & THRE) && xmit->tail != xmit->head) { UART_PUT_CHAR(uart, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); uart->port.icount.tx++; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&uart->port); } static irqreturn_t bfin_serial_rx_int(int irq, void *dev_id) { struct bfin_serial_port *uart = dev_id; while (UART_GET_LSR(uart) & DR) bfin_serial_rx_chars(uart); return IRQ_HANDLED; } static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id) { struct bfin_serial_port *uart = dev_id; #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) { uart->scts = 0; uart_handle_cts_change(&uart->port, uart->scts); } #endif spin_lock(&uart->port.lock); if (UART_GET_LSR(uart) & THRE) bfin_serial_tx_chars(uart); spin_unlock(&uart->port.lock); return IRQ_HANDLED; } #endif #ifdef CONFIG_SERIAL_BFIN_DMA static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart) { struct circ_buf *xmit = &uart->port.state->xmit; uart->tx_done = 0; if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { uart->tx_count = 0; uart->tx_done = 1; return; } if (uart->port.x_char) { UART_PUT_CHAR(uart, uart->port.x_char); uart->port.icount.tx++; uart->port.x_char = 0; } uart->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE); if (uart->tx_count > (UART_XMIT_SIZE - xmit->tail)) uart->tx_count = UART_XMIT_SIZE - xmit->tail; blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail), (unsigned long)(xmit->buf+xmit->tail+uart->tx_count)); set_dma_config(uart->tx_dma_channel, set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP, INTR_ON_BUF, DIMENSION_LINEAR, DATA_SIZE_8, DMA_SYNC_RESTART)); set_dma_start_addr(uart->tx_dma_channel, (unsigned long)(xmit->buf+xmit->tail)); set_dma_x_count(uart->tx_dma_channel, uart->tx_count); set_dma_x_modify(uart->tx_dma_channel, 1); SSYNC(); enable_dma(uart->tx_dma_channel); UART_SET_IER(uart, ETBEI); } static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart) { struct tty_struct *tty = uart->port.state->port.tty; int i, flg, status; status = UART_GET_LSR(uart); UART_CLEAR_LSR(uart); uart->port.icount.rx += CIRC_CNT(uart->rx_dma_buf.head, uart->rx_dma_buf.tail, UART_XMIT_SIZE); if (status & BI) { uart->port.icount.brk++; if (uart_handle_break(&uart->port)) goto dma_ignore_char; status &= ~(PE | FE); } if (status & PE) uart->port.icount.parity++; if (status & OE) uart->port.icount.overrun++; if (status & FE) uart->port.icount.frame++; status &= uart->port.read_status_mask; if (status & BI) flg = TTY_BREAK; else if (status & PE) flg = TTY_PARITY; else if (status & FE) flg = TTY_FRAME; else flg = TTY_NORMAL; for (i = uart->rx_dma_buf.tail; ; i++) { if (i >= UART_XMIT_SIZE) i = 0; if (i == uart->rx_dma_buf.head) break; if (!uart_handle_sysrq_char(&uart->port, uart->rx_dma_buf.buf[i])) uart_insert_char(&uart->port, status, OE, uart->rx_dma_buf.buf[i], flg); } dma_ignore_char: tty_flip_buffer_push(tty); } void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) { int x_pos, pos; dma_disable_irq_nosync(uart->rx_dma_channel); spin_lock_bh(&uart->rx_lock); /* 2D DMA RX buffer ring is used. Because curr_y_count and * curr_x_count can't be read as an atomic operation, * curr_y_count should be read before curr_x_count. When * curr_x_count is read, curr_y_count may already indicate * next buffer line. But, the position calculated here is * still indicate the old line. The wrong position data may * be smaller than current buffer tail, which cause garbages * are received if it is not prohibit. */ uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel); x_pos = get_dma_curr_xcount(uart->rx_dma_channel); uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows; if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0) uart->rx_dma_nrows = 0; x_pos = DMA_RX_XCOUNT - x_pos; if (x_pos == DMA_RX_XCOUNT) x_pos = 0; pos = uart->rx_dma_nrows * DMA_RX_XCOUNT + x_pos; /* Ignore receiving data if new position is in the same line of * current buffer tail and small. */ if (pos > uart->rx_dma_buf.tail || uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) { uart->rx_dma_buf.head = pos; bfin_serial_dma_rx_chars(uart); uart->rx_dma_buf.tail = uart->rx_dma_buf.head; } spin_unlock_bh(&uart->rx_lock); dma_enable_irq(uart->rx_dma_channel); mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES); }