static void rs_interruptRx(int irq, void *dev_id, struct pt_regs *regs) { struct s3c3410_serial *info = &s3c3410_info; struct tty_struct *tty = info->tty; unsigned int count; volatile u_int8_t status, fifo_status; if (!info || !tty || (!(info->flags & S_INITIALIZED))) return; if ((tty->flip.count + RX_FIFO_DEPTH) >= TTY_FLIPBUF_SIZE) queue_task_irq_off(&tty->flip.tqueue, &tq_timer); count = RX_FIFO_DEPTH; do { status = inb(S3C3410X_UART_BASE+S3C3410X_USTAT); if (!(status & USTAT_RFDR)) break; /* check all error flags and accept data if valid */ fifo_status = inb(S3C3410X_UART_BASE+S3C3410X_UFSTAT); if (!(status & USTAT_ERROR) && !(fifo_status & UFSTAT_ERROR)) *tty->flip.flag_buf_ptr = TTY_NORMAL; else { if (fifo_status & UFSTAT_RFF) { *tty->flip.flag_buf_ptr = TTY_NORMAL; handle_status (info, UFSTAT_RFF); } if (status & USTAT_OE) { *tty->flip.flag_buf_ptr = TTY_OVERRUN; handle_status (info, USTAT_OE); } if (status & USTAT_BD) { *tty->flip.flag_buf_ptr = TTY_BREAK; handle_status (info, USTAT_BD); } if (status & USTAT_PE) { *tty->flip.flag_buf_ptr = TTY_PARITY; handle_status (info, USTAT_PE); } if (status & USTAT_FE) { *tty->flip.flag_buf_ptr = TTY_FRAME; handle_status (info, USTAT_FE); } } *tty->flip.char_buf_ptr++ = inb(S3C3410X_UART_BASE+S3C3410X_URXH_B); tty->flip.flag_buf_ptr++; tty->flip.count++; } while ((--count > 0) && !(status & USTAT_ERROR) && !(fifo_status & UFSTAT_ERROR) ); // if (fifo_status & (U_RFOV | U_E_RxTO)) // handle_status (info, (U_RFOV | U_E_RxTO)); queue_task_irq_off(&tty->flip.tqueue, &tq_timer); }
/* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */ static _INLINE_ void rs_sched_event(struct LEON_serial *info, int event) { info->event |= 1 << event; queue_task_irq_off(&info->tqueue, &tq_serial); mark_bh(SERIAL_BH); }
/* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */ static _INLINE_ void rs_sched_event(struct sci_struct *sci, int event) { sci->info->event |= 1 << event; queue_task_irq_off(&sci->info->tqueue, &tq_serial); mark_bh(SERIAL_BH); }
void rs_interrupt(int irq, void * dev_id, struct pt_regs * regs) { char status; struct cnxt_serial * info = &uart_info; struct uart_regs *uart = uart_info.uart; status = (uart->iir & 0x0f); /* only concerned w/ lower nibble */ if (status == ISR_Tx_Rdy_Source) { transmit_chars(info); } if ((status == ISR_Rx_Rdy_Source) || (status == ISR_Rx_Rdy_TO_Src )){ receive_chars(info,status); } #if 0 if(!info->use_ints){ serialpoll.data = (void *)&sp_uart_info; queue_task_irq_off(&serialpoll, &tq_timer); } #endif return; }
static void serial_handle_oob_event(struct async_struct *info, mach_port_t device_port) { kern_return_t kr; mach_msg_type_number_t count; struct tty_out_of_band toob; count = TTY_OUT_OF_BAND_COUNT; kr = device_get_status(device_port, TTY_OUT_OF_BAND, (dev_status_t) &toob, &count); if (kr != D_SUCCESS) { MACH3_DEBUG(1, kr, ("serial_handle_oob_event(%d):device_get_status",kr )); return; } switch (toob.toob_event) { case TOOB_NO_EVENT: break; case TOOB_BREAK: serial_put_status_byte(info, TTY_BREAK); break; case TOOB_BAD_PARITY: serial_put_status_byte(info, TTY_PARITY); break; case TOOB_FLUSHED: break; case TOOB_CARRIER: if (toob.toob_arg) { info->flags |= ASYNC_DCD_PRESENT; wake_up_interruptible(&info->open_wait); } else { info->flags &= ~ASYNC_DCD_PRESENT; if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_CALLOUT_NOHUP))) { #ifdef SERIAL_DEBUG_OPEN printk("scheduling hangup..."); #endif queue_task_irq_off(&info->tqueue_hangup, &tq_scheduler); } } break; default: printk("serial_handle_oob_event: unknown event 0x%x\n", toob.toob_event); break; } return; }
static _INLINE_ void receive_chars(struct gbatxt_serial *info, struct pt_regs *regs, unsigned short rx) { volatile unsigned char *uartp; struct tty_struct *tty = info->tty; unsigned char status, ch; if (!tty) return; #if defined(CONFIG_LEDMAN) ledman_cmd(LEDMAN_CMD_SET, info->line ? LEDMAN_COM2_RX : LEDMAN_COM1_RX); #endif uartp = (volatile unsigned char *) info->addr; while ((status = uartp[MCFUART_USR]) & MCFUART_USR_RXREADY) { if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; ch = uartp[MCFUART_URB]; info->stats.rx++; #ifdef CONFIG_MAGIC_SYSRQ if (gbatxt_console_inited && (info->line == gbatxt_console_port)) { if (magic_sysrq_key(ch)) continue; } #endif tty->flip.count++; if (status & MCFUART_USR_RXERR) uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETERR; if (status & MCFUART_USR_RXBREAK) { info->stats.rxbreak++; *tty->flip.flag_buf_ptr++ = TTY_BREAK; } else if (status & MCFUART_USR_RXPARITY) { info->stats.rxparity++; *tty->flip.flag_buf_ptr++ = TTY_PARITY; } else if (status & MCFUART_USR_RXOVERRUN) { info->stats.rxoverrun++; *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; } else if (status & MCFUART_USR_RXFRAMING) { info->stats.rxframing++; *tty->flip.flag_buf_ptr++ = TTY_FRAME; } else { *tty->flip.flag_buf_ptr++ = 0; } *tty->flip.char_buf_ptr++ = ch; } queue_task_irq_off(&tty->flip.tqueue, &tq_timer); return; }
/* * Change of state on a DCD line. */ void gbatxt_modem_change(struct gbatxt_serial *info, int dcd) { if (info->count == 0) return; if (info->flags & ASYNC_CHECK_CD) { if (dcd) { wake_up_interruptible(&info->open_wait); } else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_CALLOUT_NOHUP))) { queue_task_irq_off(&info->tqueue_hangup, &tq_scheduler); } } }
static void serial_put_status_byte(struct async_struct *info, unsigned char byte) { if (info->tty == NULL) return; if (info->tty->flip.count >= TTY_FLIPBUF_SIZE) return; *info->tty->flip.flag_buf_ptr++ = byte; *info->tty->flip.char_buf_ptr++ = 0; info->tty->flip.count++; queue_task_irq_off(&info->tty->flip.tqueue, &tq_timer); }
static _INLINE_ void receive_chars(struct sci_struct *sci) { struct tty_struct *tty = sci->info->tty; unsigned char ch; do { ch = sci_inp(sci, SCI_RDR); sci_outp(sci,SCI_SSR,sci_in(sci,SCI_SSR) & ~SCI_SSR_RDRF); if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; tty->flip.count++; *tty->flip.flag_buf_ptr++ = 0; *tty->flip.char_buf_ptr++ = ch; } while (sci_inp(sci, SCI_SSR) & SCI_SSR_RDRF); queue_task_irq_off(&tty->flip.tqueue, &tq_timer); #ifdef SERIAL_DEBUG_INTR printk("DR..."); #endif }
static _INLINE_ void receive_chars(struct LEON_serial *info, struct pt_regs *regs, unsigned short rx) { struct tty_struct *tty = info->tty; unsigned char ch; /* * This do { } while() loop will get ALL chars out of Rx FIFO */ do { ch = leon->uartdata1; if(info->is_cons) { if(0 /* LEON does not report break */ & rx) { /* whee, break received */ status_handle(info, rx); return; } else if (ch == 0x10) { /* ^P */ show_state(); show_free_areas(); show_buffers(); show_net_buffers(); return; } else if (ch == 0x12) { /* ^R */ hard_reset_now(); return; } /* It is a 'keyboard interrupt' ;-) */ wake_up(&keypress_wait); } if(!tty) goto clear_and_exit; /* * Make sure that we do not overflow the buffer */ if (tty->flip.count >= TTY_FLIPBUF_SIZE) { queue_task_irq_off(&tty->flip.tqueue, &tq_timer); return; } if(rx & USTAT_PE) { *tty->flip.flag_buf_ptr++ = TTY_PARITY; status_handle(info, rx); } else if(rx & USTAT_OV) { *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; status_handle(info, rx); } else if(rx & USTAT_FE) { *tty->flip.flag_buf_ptr++ = TTY_FRAME; status_handle(info, rx); } else { *tty->flip.flag_buf_ptr++ = 0; /* XXX */ } *tty->flip.char_buf_ptr++ = ch; tty->flip.count++; } while((rx = leon->uartstatus1) & USTAT_DR); queue_task_irq_off(&tty->flip.tqueue, &tq_timer); clear_and_exit: return; }
/* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */ static _INLINE_ void gbatxt_sched_event(struct gbatxt_serial *info, int event) { info->event |= 1 << event; queue_task_irq_off(&info->tqueue, &mcf_tq_serial); mark_bh(CM206_BH); }
static inline void receive_chars(struct cnxt_serial *info, unsigned long status) { unsigned char ch; struct uart_regs *uart = info->uart; #if 0 // hack to receive chars by polling from anywhere struct tty_struct *tty = info->tty; if (!(info->flags & S_INITIALIZED)) return; #else struct tty_struct *tty = info->tty; if (!(info->flags & S_INITIALIZED)) return; #endif ch = (unsigned char)uart->fifo; if(info->is_cons) { if (ch == 0x10) { show_state(); show_free_areas(); show_buffers(); //show_net_buffers(); return; } else if (ch == 0x12) { HARD_RESET_NOW(); return; } } /* Look for kgdb 'stop' character, consult the gdb documentation * for remote target debugging and arch/sparc/kernel/sparc-stub.c * to see how all this works. */ if(!tty) { printk("no tty\n"); goto clear_and_exit; } #if 0 if (tty->flip.count >= TTY_FLIPBUF_SIZE) queue_task_irq_off(&tty->flip.tqueue, &tq_timer); tty->flip.count++; if(status & US_PARE) *tty->flip.flag_buf_ptr++ = TTY_PARITY; else if(status & US_OVRE) *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; else if(status & US_FRAME) *tty->flip.flag_buf_ptr++ = TTY_FRAME; else *tty->flip.flag_buf_ptr++ = 0; #endif tty->flip.count++; *tty->flip.flag_buf_ptr++ = 0; *tty->flip.char_buf_ptr++ = ch; queue_task(&tty->flip.tqueue, &tq_timer); clear_and_exit: return; }
void * serial_read_thread( void *arg) { struct server_thread_priv_data priv_data; kern_return_t kr; struct async_struct *info; int line, wait_loop, count; io_buf_ptr_inband_t inbuf; /* 128 chars */ mach_msg_type_number_t data_count; mach_port_t device_port; cthread_set_name(cthread_self(), "serial read"); server_thread_set_priv_data(cthread_self(), &priv_data); line = (int) arg; info = rs_table + line; uniproc_enter(); device_port = info->device_port; for (;;) { data_count = sizeof inbuf; uniproc_exit(); kr = device_read_inband(device_port, 0, /* mode */ 0, /* recnum */ sizeof inbuf, inbuf, &data_count); uniproc_enter(); if (kr == D_OUT_OF_BAND) { serial_handle_oob_event(info, device_port); continue; } else if (kr != D_SUCCESS) { /* Something happened.. simply shutdown the line.. */ if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_CALLOUT_NOHUP))) queue_task_irq_off(&info->tqueue_hangup, &tq_scheduler); uniproc_exit(); cthread_detach(cthread_self()); cthread_exit((void *) 0); /* NEVER REACHED */ } if (data_count <= 0) continue; /* * Its very possible with the Power Mac * to overflow the Linux TTY buffers. * (A serial interrupt can present up to * 8K worth of data in one shot) * * The following loops attempts to give the * PPP line disc. a chance to clear the queue * out. */ for (wait_loop = 0; wait_loop < 6; wait_loop++) { /* Check to make sure the tty did not * go away.. */ if (info->tty == NULL) break; if ((info->tty->flip.count+data_count) < TTY_FLIPBUF_SIZE) break; /* Try to give another thread some time.. */ osfmach3_yield(); } if (info->tty == NULL) continue; count = MIN(TTY_FLIPBUF_SIZE + info->tty->flip.count, data_count); if (count <= 0) continue; info->last_active = jiffies; info->tty->flip.count += count; memcpy(info->tty->flip.char_buf_ptr, inbuf, count); memset(info->tty->flip.flag_buf_ptr, 0, count); info->tty->flip.flag_buf_ptr += count; info->tty->flip.char_buf_ptr += count; queue_task_irq_off(&info->tty->flip.tqueue, &tq_timer); } }