/* * Send characters to device-specific code */ void rtems_termios_puts ( const void *_buf, size_t len, struct rtems_termios_tty *tty) { const char *buf = _buf; unsigned int newHead; rtems_interrupt_lock_context lock_context; rtems_status_code sc; if (tty->handler.mode == TERMIOS_POLLED) { (*tty->handler.write)(tty, buf, len); return; } newHead = tty->rawOutBuf.Head; while (len) { /* * Performance improvement could be made here. * Copy multiple bytes to raw buffer: * if (len > 1) && (space to buffer end, or tail > 1) * ncopy = MIN (len, space to buffer end or tail) * memcpy (raw buffer, buf, ncopy) * buf += ncopy * len -= ncopy * * To minimize latency, the memcpy should be done * with interrupts enabled. */ newHead = (newHead + 1) % tty->rawOutBuf.Size; rtems_termios_interrupt_lock_acquire (tty, &lock_context); while (newHead == tty->rawOutBuf.Tail) { tty->rawOutBufState = rob_wait; rtems_termios_interrupt_lock_release (tty, &lock_context); sc = rtems_semaphore_obtain( tty->rawOutBuf.Semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); rtems_termios_interrupt_lock_acquire (tty, &lock_context); } tty->rawOutBuf.theBuf[tty->rawOutBuf.Head] = *buf++; tty->rawOutBuf.Head = newHead; if (tty->rawOutBufState == rob_idle) { /* check, whether XOFF has been received */ if (!(tty->flow_ctrl & FL_ORCVXOF)) { (*tty->handler.write)( tty, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1); } else { /* remember that output has been stopped due to flow ctrl*/ tty->flow_ctrl |= FL_OSTOP; } tty->rawOutBufState = rob_busy; } rtems_termios_interrupt_lock_release (tty, &lock_context); len--; } }
static void flushInput (struct rtems_termios_tty *tty) { rtems_interrupt_lock_context lock_context; rtems_termios_interrupt_lock_acquire (tty, &lock_context); tty->rawInBuf.Tail = 0; tty->rawInBuf.Head = 0; rtems_termios_interrupt_lock_release (tty, &lock_context); }
static void flushOutput (struct rtems_termios_tty *tty) { rtems_interrupt_lock_context lock_context; rtems_termios_interrupt_lock_acquire (tty, &lock_context); tty->rawOutBuf.Tail = 0; tty->rawOutBuf.Head = 0; tty->rawOutBufState = rob_idle; rtems_termios_interrupt_lock_release (tty, &lock_context); }
/* * Drain output queue */ static void drainOutput (struct rtems_termios_tty *tty) { rtems_interrupt_lock_context lock_context; rtems_status_code sc; if (tty->handler.mode != TERMIOS_POLLED) { rtems_termios_interrupt_lock_acquire (tty, &lock_context); while (tty->rawOutBuf.Tail != tty->rawOutBuf.Head) { tty->rawOutBufState = rob_wait; rtems_termios_interrupt_lock_release (tty, &lock_context); sc = rtems_semaphore_obtain( tty->rawOutBuf.Semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); rtems_termios_interrupt_lock_acquire (tty, &lock_context); } rtems_termios_interrupt_lock_release (tty, &lock_context); } }
static int leon3_console_last_close(int major, int minor, void *arg) { struct rtems_termios_tty *tty = leon3_console_get_tty(arg); struct apbuart_priv *uart = leon3_console_get_uart(minor); rtems_interrupt_level level; /* Turn off RX interrupts */ rtems_termios_interrupt_lock_acquire(tty, level); uart->regs->ctrl &= ~(LEON_REG_UART_CTRL_RI); rtems_termios_interrupt_lock_release(tty, level); /**** Flush device ****/ while (uart->sending) { /* Wait until all data has been sent */ } /* uninstall ISR */ rtems_interrupt_handler_remove(uart->irq, leon3_console_isr, uart); return 0; }
static void termios_set_flowctrl(struct rtems_termios_tty *tty) { rtems_interrupt_lock_context lock_context; /* * check for flow control options to be switched off */ /* check for outgoing XON/XOFF flow control switched off */ if (( tty->flow_ctrl & FL_MDXON) && !(tty->termios.c_iflag & IXON)) { /* clear related flags in flow_ctrl */ tty->flow_ctrl &= ~(FL_MDXON | FL_ORCVXOF); /* has output been stopped due to received XOFF? */ if (tty->flow_ctrl & FL_OSTOP) { /* disable interrupts */ rtems_termios_interrupt_lock_acquire (tty, &lock_context); tty->flow_ctrl &= ~FL_OSTOP; /* check for chars in output buffer (or rob_state?) */ if (tty->rawOutBufState != rob_idle) { /* if chars available, call write function... */ (*tty->handler.write)( tty, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1); } /* reenable interrupts */ rtems_termios_interrupt_lock_release (tty, &lock_context); } } /* check for incoming XON/XOFF flow control switched off */ if (( tty->flow_ctrl & FL_MDXOF) && !(tty->termios.c_iflag & IXOFF)) { /* clear related flags in flow_ctrl */ tty->flow_ctrl &= ~(FL_MDXOF); /* FIXME: what happens, if we had sent XOFF but not yet XON? */ tty->flow_ctrl &= ~(FL_ISNTXOF); } /* check for incoming RTS/CTS flow control switched off */ if (( tty->flow_ctrl & FL_MDRTS) && !(tty->termios.c_cflag & CRTSCTS)) { /* clear related flags in flow_ctrl */ tty->flow_ctrl &= ~(FL_MDRTS); /* restart remote Tx, if it was stopped */ if ((tty->flow_ctrl & FL_IRTSOFF) && (tty->handler.start_remote_tx != NULL)) { tty->handler.start_remote_tx(tty); } tty->flow_ctrl &= ~(FL_IRTSOFF); } /* * check for flow control options to be switched on */ /* check for incoming RTS/CTS flow control switched on */ if (tty->termios.c_cflag & CRTSCTS) { tty->flow_ctrl |= FL_MDRTS; } /* check for incoming XON/XOF flow control switched on */ if (tty->termios.c_iflag & IXOFF) { tty->flow_ctrl |= FL_MDXOF; } /* check for outgoing XON/XOF flow control switched on */ if (tty->termios.c_iflag & IXON) { tty->flow_ctrl |= FL_MDXON; } }
/* * in task-driven mode, this function is called in Tx task context * in interrupt-driven mode, this function is called in TxIRQ context */ static int rtems_termios_refill_transmitter (struct rtems_termios_tty *tty) { bool wakeUpWriterTask = false; unsigned int newTail; int nToSend; rtems_interrupt_lock_context lock_context; int len; rtems_termios_interrupt_lock_acquire (tty, &lock_context); /* check for XOF/XON to send */ if ((tty->flow_ctrl & (FL_MDXOF | FL_IREQXOF | FL_ISNTXOF)) == (FL_MDXOF | FL_IREQXOF)) { /* XOFF should be sent now... */ (*tty->handler.write)(tty, (void *)&(tty->termios.c_cc[VSTOP]), 1); tty->t_dqlen--; tty->flow_ctrl |= FL_ISNTXOF; nToSend = 1; } else if ((tty->flow_ctrl & (FL_IREQXOF | FL_ISNTXOF)) == FL_ISNTXOF) { /* NOTE: send XON even, if no longer in XON/XOFF mode... */ /* XON should be sent now... */ /* * FIXME: this .write call will generate another * dequeue callback. This will advance the "Tail" in the data * buffer, although the corresponding data is not yet out! * Therefore the dequeue "length" should be reduced by 1 */ (*tty->handler.write)(tty, (void *)&(tty->termios.c_cc[VSTART]), 1); tty->t_dqlen--; tty->flow_ctrl &= ~FL_ISNTXOF; nToSend = 1; } else if ( tty->rawOutBuf.Head == tty->rawOutBuf.Tail ) { /* * buffer was empty */ if (tty->rawOutBufState == rob_wait) { /* * this should never happen... */ wakeUpWriterTask = true; } (*tty->handler.write) (tty, NULL, 0); nToSend = 0; } else { len = tty->t_dqlen; tty->t_dqlen = 0; newTail = (tty->rawOutBuf.Tail + len) % tty->rawOutBuf.Size; tty->rawOutBuf.Tail = newTail; if (tty->rawOutBufState == rob_wait) { /* * wake up any pending writer task */ wakeUpWriterTask = true; } if (newTail == tty->rawOutBuf.Head) { /* * Buffer has become empty */ tty->rawOutBufState = rob_idle; (*tty->handler.write) (tty, NULL, 0); nToSend = 0; /* * check to see if snd wakeup callback was set */ if ( tty->tty_snd.sw_pfn != NULL) { (*tty->tty_snd.sw_pfn)(&tty->termios, tty->tty_snd.sw_arg); } } /* check, whether output should stop due to received XOFF */ else if ((tty->flow_ctrl & (FL_MDXON | FL_ORCVXOF)) == (FL_MDXON | FL_ORCVXOF)) { /* Buffer not empty, but output stops due to XOFF */ /* set flag, that output has been stopped */ tty->flow_ctrl |= FL_OSTOP; tty->rawOutBufState = rob_busy; /*apm*/ (*tty->handler.write) (tty, NULL, 0); nToSend = 0; } else { /* * Buffer not empty, start tranmitter */ if (newTail > tty->rawOutBuf.Head) nToSend = tty->rawOutBuf.Size - newTail; else nToSend = tty->rawOutBuf.Head - newTail; /* when flow control XON or XOF, don't send blocks of data */ /* to allow fast reaction on incoming flow ctrl and low latency*/ /* for outgoing flow control */ if (tty->flow_ctrl & (FL_MDXON | FL_MDXOF)) { nToSend = 1; } tty->rawOutBufState = rob_busy; /*apm*/ (*tty->handler.write)( tty, &tty->rawOutBuf.theBuf[newTail], nToSend); } tty->rawOutBuf.Tail = newTail; /*apm*/ } rtems_termios_interrupt_lock_release (tty, &lock_context); if (wakeUpWriterTask) { rtems_semaphore_release (tty->rawOutBuf.Semaphore); } return nToSend; }
/* * Place characters on raw queue. * NOTE: This routine runs in the context of the * device receive interrupt handler. * Returns the number of characters dropped because of overflow. */ int rtems_termios_enqueue_raw_characters (void *ttyp, const char *buf, int len) { struct rtems_termios_tty *tty = ttyp; unsigned int newTail; char c; int dropped = 0; bool flow_rcv = false; /* true, if flow control char received */ rtems_interrupt_lock_context lock_context; if (rtems_termios_linesw[tty->t_line].l_rint != NULL) { while (len--) { c = *buf++; rtems_termios_linesw[tty->t_line].l_rint(c,tty); } /* * check to see if rcv wakeup callback was set */ if (( !tty->tty_rcvwakeup ) && ( tty->tty_rcv.sw_pfn != NULL )) { (*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg); tty->tty_rcvwakeup = 1; } return 0; } while (len--) { c = *buf++; /* FIXME: implement IXANY: any character restarts output */ /* if incoming XON/XOFF controls outgoing stream: */ if (tty->flow_ctrl & FL_MDXON) { /* if received char is V_STOP and V_START (both are equal value) */ if (c == tty->termios.c_cc[VSTOP]) { if (c == tty->termios.c_cc[VSTART]) { /* received VSTOP and VSTART==VSTOP? */ /* then toggle "stop output" status */ tty->flow_ctrl = tty->flow_ctrl ^ FL_ORCVXOF; } else { /* VSTOP received (other code than VSTART) */ /* stop output */ tty->flow_ctrl |= FL_ORCVXOF; } flow_rcv = true; } else if (c == tty->termios.c_cc[VSTART]) { /* VSTART received */ /* restart output */ tty->flow_ctrl &= ~FL_ORCVXOF; flow_rcv = true; } } if (flow_rcv) { /* restart output according to FL_ORCVXOF flag */ if ((tty->flow_ctrl & (FL_ORCVXOF | FL_OSTOP)) == FL_OSTOP) { /* disable interrupts */ rtems_termios_interrupt_lock_acquire (tty, &lock_context); tty->flow_ctrl &= ~FL_OSTOP; /* check for chars in output buffer (or rob_state?) */ if (tty->rawOutBufState != rob_idle) { /* if chars available, call write function... */ (*tty->handler.write)( tty, &tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail], 1); } /* reenable interrupts */ rtems_termios_interrupt_lock_release (tty, &lock_context); } } else { newTail = (tty->rawInBuf.Tail + 1) % tty->rawInBuf.Size; /* if chars_in_buffer > highwater */ rtems_termios_interrupt_lock_acquire (tty, &lock_context); if ((((newTail - tty->rawInBuf.Head + tty->rawInBuf.Size) % tty->rawInBuf.Size) > tty->highwater) && !(tty->flow_ctrl & FL_IREQXOF)) { /* incoming data stream should be stopped */ tty->flow_ctrl |= FL_IREQXOF; if ((tty->flow_ctrl & (FL_MDXOF | FL_ISNTXOF)) == (FL_MDXOF ) ) { if ((tty->flow_ctrl & FL_OSTOP) || (tty->rawOutBufState == rob_idle)) { /* if tx is stopped due to XOFF or out of data */ /* call write function here */ tty->flow_ctrl |= FL_ISNTXOF; (*tty->handler.write)(tty, (void *)&(tty->termios.c_cc[VSTOP]), 1); } } else if ((tty->flow_ctrl & (FL_MDRTS | FL_IRTSOFF)) == (FL_MDRTS) ) { tty->flow_ctrl |= FL_IRTSOFF; /* deactivate RTS line */ if (tty->handler.stop_remote_tx != NULL) { tty->handler.stop_remote_tx(tty); } } } /* reenable interrupts */ rtems_termios_interrupt_lock_release (tty, &lock_context); if (newTail == tty->rawInBuf.Head) { dropped++; } else { tty->rawInBuf.theBuf[newTail] = c; tty->rawInBuf.Tail = newTail; /* * check to see if rcv wakeup callback was set */ if (( !tty->tty_rcvwakeup ) && ( tty->tty_rcv.sw_pfn != NULL )) { (*tty->tty_rcv.sw_pfn)(&tty->termios, tty->tty_rcv.sw_arg); tty->tty_rcvwakeup = 1; } } } } tty->rawInBufDropped += dropped; rtems_semaphore_release (tty->rawInBuf.Semaphore); return dropped; }