📄 termios.c
字号:
/* * TERMIOS serial line support * * Author: * W. Eric Norum * Saskatchewan Accelerator Laboratory * University of Saskatchewan * Saskatoon, Saskatchewan, CANADA * eric@skatter.usask.ca * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. * * $Id: termios.c,v 1.39.2.6 2006/03/07 22:22:46 joel Exp $ */#if HAVE_CONFIG_H#include "config.h"#endif#include <rtems.h>#include <rtems/libio.h>#include <ctype.h>#include <errno.h>#include <stdio.h>#include <stdlib.h>#include <termios.h>#include <unistd.h>#include <sys/ttycom.h>#include <rtems/termiostypes.h>/* * FreeBSD does not support a full POSIX termios so we have to help it out */#if !HAVE_DECL_XTABS#define XTABS 0#endif#if !HAVE_DECL_ONLRET#define ONLRET 0#endif#if !HAVE_DECL_ONOCR#define ONOCR 0#endif#if !HAVE_DECL_TABDLY#define TABDLY 0#endif#if !HAVE_DECL_OLCUC#define OLCUC 0#endif#if !HAVE_DECL_ILCUC#define ILCUC 0#endif#if !HAVE_DECL_OCRNL#define OCRNL 0#endif#if !HAVE_DECL_IUCLC#define IUCLC 0#endif#if !HAVE_DECL_ECHOPRT#define ECHOPRT 0#endif/* * The size of the cooked buffer */#define CBUFSIZE (rtems_termios_cbufsize)/* * The sizes of the raw message buffers. * On most architectures it is quite a bit more * efficient if these are powers of two. */#define RAW_INPUT_BUFFER_SIZE (rtems_termios_raw_input_size)#define RAW_OUTPUT_BUFFER_SIZE (rtems_termios_raw_output_size)/* fields for "flow_ctrl" status */#define FL_IREQXOF 1 /* input queue requests stop of incoming data */#define FL_ISNTXOF 2 /* XOFF has been sent to other side of line */#define FL_IRTSOFF 4 /* RTS has been turned off for other side.. */#define FL_ORCVXOF 0x10 /* XOFF has been received */#define FL_OSTOP 0x20 /* output has been stopped due to XOFF */#define FL_MDRTS 0x100 /* input controlled with RTS/CTS handshake */#define FL_MDXON 0x200 /* input controlled with XON/XOFF protocol */#define FL_MDXOF 0x400 /* output controlled with XON/XOFF protocol */#define NODISC(n) \ { NULL, NULL, NULL, NULL, \ NULL, NULL, NULL, NULL }/* * FIXME: change linesw entries consistant with linesw entry usage... */struct linesw linesw[MAXLDISC] ={ NODISC(0), /* 0- termios-built-in */ NODISC(1), /* 1- defunct */ NODISC(2), /* 2- NTTYDISC */ NODISC(3), /* TABLDISC */ NODISC(4), /* SLIPDISC */ NODISC(5), /* PPPDISC */ NODISC(6), /* loadable */ NODISC(7), /* loadable */};int nlinesw = sizeof (linesw) / sizeof (linesw[0]);extern struct rtems_termios_tty *rtems_termios_ttyHead;extern struct rtems_termios_tty *rtems_termios_ttyTail;extern rtems_id rtems_termios_ttyMutex;static int rtems_termios_cbufsize = 256;static int rtems_termios_raw_input_size = 128;static int rtems_termios_raw_output_size = 64;static rtems_task rtems_termios_rxdaemon(rtems_task_argument argument);static rtems_task rtems_termios_txdaemon(rtems_task_argument argument);/* * some constants for I/O daemon task creation */#define TERMIOS_TXTASK_PRIO 10#define TERMIOS_RXTASK_PRIO 9#define TERMIOS_TXTASK_STACKSIZE 1024#define TERMIOS_RXTASK_STACKSIZE 1024/* * some events to be sent to the I/O tasks */#define TERMIOS_TX_START_EVENT RTEMS_EVENT_1#define TERMIOS_TX_TERMINATE_EVENT RTEMS_EVENT_0#define TERMIOS_RX_PROC_EVENT RTEMS_EVENT_1#define TERMIOS_RX_TERMINATE_EVENT RTEMS_EVENT_0/* * Open a termios device */rtems_status_codertems_termios_open ( rtems_device_major_number major, rtems_device_minor_number minor, void *arg, const rtems_termios_callbacks *callbacks ){ rtems_status_code sc; rtems_libio_open_close_args_t *args = arg; struct rtems_termios_tty *tty; /* * See if the device has already been opened */ sc = rtems_semaphore_obtain (rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (sc != RTEMS_SUCCESSFUL) return sc; for (tty = rtems_termios_ttyHead ; tty != NULL ; tty = tty->forw) { if ((tty->major == major) && (tty->minor == minor)) break; } if (tty == NULL) { static char c = 'a'; /* * Create a new device */ tty = calloc (1, sizeof (struct rtems_termios_tty)); if (tty == NULL) { rtems_semaphore_release (rtems_termios_ttyMutex); return RTEMS_NO_MEMORY; } /* * allocate raw input buffer */ tty->rawInBuf.Size = RAW_INPUT_BUFFER_SIZE; tty->rawInBuf.theBuf = malloc (tty->rawInBuf.Size); if (tty->rawInBuf.theBuf == NULL) { free(tty); rtems_semaphore_release (rtems_termios_ttyMutex); return RTEMS_NO_MEMORY; } /* * allocate raw output buffer */ tty->rawOutBuf.Size = RAW_OUTPUT_BUFFER_SIZE; tty->rawOutBuf.theBuf = malloc (tty->rawOutBuf.Size); if (tty->rawOutBuf.theBuf == NULL) { free((void *)(tty->rawInBuf.theBuf)); free(tty); rtems_semaphore_release (rtems_termios_ttyMutex); return RTEMS_NO_MEMORY; } /* * allocate cooked buffer */ tty->cbuf = malloc (CBUFSIZE); if (tty->cbuf == NULL) { free((void *)(tty->rawOutBuf.theBuf)); free((void *)(tty->rawInBuf.theBuf)); free(tty); rtems_semaphore_release (rtems_termios_ttyMutex); return RTEMS_NO_MEMORY; } /* * Initialize wakeup callbacks */ tty->tty_snd.sw_pfn = NULL; tty->tty_snd.sw_arg = NULL; tty->tty_rcv.sw_pfn = NULL; tty->tty_rcv.sw_arg = NULL; tty->tty_rcvwakeup = 0; /* * link tty */ tty->forw = rtems_termios_ttyHead; tty->back = NULL; if (rtems_termios_ttyHead != NULL) rtems_termios_ttyHead->back = tty; rtems_termios_ttyHead = tty; if (rtems_termios_ttyTail == NULL) rtems_termios_ttyTail = tty; tty->minor = minor; tty->major = major; /* * Set up mutex semaphores */ sc = rtems_semaphore_create ( rtems_build_name ('T', 'R', 'i', c), 1, RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY, RTEMS_NO_PRIORITY, &tty->isem); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); sc = rtems_semaphore_create ( rtems_build_name ('T', 'R', 'o', c), 1, RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY, RTEMS_NO_PRIORITY, &tty->osem); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); sc = rtems_semaphore_create ( rtems_build_name ('T', 'R', 'x', c), 0, RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_FIFO, RTEMS_NO_PRIORITY, &tty->rawOutBuf.Semaphore); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); tty->rawOutBufState = rob_idle; /* * Set callbacks */ tty->device = *callbacks; /* * Create I/O tasks */ if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) { sc = rtems_task_create ( rtems_build_name ('T', 'x', 'T', c), TERMIOS_TXTASK_PRIO, TERMIOS_TXTASK_STACKSIZE, RTEMS_NO_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR, RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL, &tty->txTaskId); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); sc = rtems_task_create ( rtems_build_name ('R', 'x', 'T', c), TERMIOS_RXTASK_PRIO, TERMIOS_RXTASK_STACKSIZE, RTEMS_NO_PREEMPT | RTEMS_NO_TIMESLICE | RTEMS_NO_ASR, RTEMS_NO_FLOATING_POINT | RTEMS_LOCAL, &tty->rxTaskId); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); } if ((tty->device.pollRead == NULL) || (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN)){ sc = rtems_semaphore_create ( rtems_build_name ('T', 'R', 'r', c), 0, RTEMS_COUNTING_SEMAPHORE | RTEMS_PRIORITY, RTEMS_NO_PRIORITY, &tty->rawInBuf.Semaphore); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); } /* * Set default parameters */ tty->termios.c_iflag = BRKINT | ICRNL | IXON | IMAXBEL; tty->termios.c_oflag = OPOST | ONLCR | XTABS; tty->termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL; tty->termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOK | ECHOE | ECHOCTL; tty->termios.c_cc[VINTR] = '\003'; tty->termios.c_cc[VQUIT] = '\034'; tty->termios.c_cc[VERASE] = '\177'; tty->termios.c_cc[VKILL] = '\025'; tty->termios.c_cc[VEOF] = '\004'; tty->termios.c_cc[VEOL] = '\000'; tty->termios.c_cc[VEOL2] = '\000'; tty->termios.c_cc[VSTART] = '\021'; tty->termios.c_cc[VSTOP] = '\023'; tty->termios.c_cc[VSUSP] = '\032'; tty->termios.c_cc[VREPRINT] = '\022'; tty->termios.c_cc[VDISCARD] = '\017'; tty->termios.c_cc[VWERASE] = '\027'; tty->termios.c_cc[VLNEXT] = '\026'; /* start with no flow control, clear flow control flags */ tty->flow_ctrl = 0; /* * set low/highwater mark for XON/XOFF support */ tty->lowwater = tty->rawInBuf.Size * 1/2; tty->highwater = tty->rawInBuf.Size * 3/4; /* * Bump name characer */ if (c++ == 'z') c = 'a'; } args->iop->data1 = tty; if (!tty->refcount++) { if (tty->device.firstOpen) (*tty->device.firstOpen)(major, minor, arg); /* * start I/O tasks, if needed */ if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) { sc = rtems_task_start(tty->rxTaskId, rtems_termios_rxdaemon, (rtems_task_argument)tty); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); sc = rtems_task_start(tty->txTaskId, rtems_termios_txdaemon, (rtems_task_argument)tty); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); } } rtems_semaphore_release (rtems_termios_ttyMutex); return RTEMS_SUCCESSFUL;}/* * Drain output queue */static voiddrainOutput (struct rtems_termios_tty *tty){ rtems_interrupt_level level; rtems_status_code sc; if (tty->device.outputUsesInterrupts != TERMIOS_POLLED) { rtems_interrupt_disable (level); while (tty->rawOutBuf.Tail != tty->rawOutBuf.Head) { tty->rawOutBufState = rob_wait; rtems_interrupt_enable (level); sc = rtems_semaphore_obtain (tty->rawOutBuf.Semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); rtems_interrupt_disable (level); } rtems_interrupt_enable (level); }}rtems_status_codertems_termios_close (void *arg){ rtems_libio_open_close_args_t *args = arg; struct rtems_termios_tty *tty = args->iop->data1; rtems_status_code sc; sc = rtems_semaphore_obtain (rtems_termios_ttyMutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); if (--tty->refcount == 0) { if (linesw[tty->t_line].l_close != NULL) { /* * call discipline-specific close */ sc = linesw[tty->t_line].l_close(tty); } else { /* * default: just flush output buffer */ drainOutput (tty); } if (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN) { /* * send "terminate" to I/O tasks */ sc = rtems_event_send( tty->rxTaskId, TERMIOS_RX_TERMINATE_EVENT); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); sc = rtems_event_send( tty->txTaskId, TERMIOS_TX_TERMINATE_EVENT); if (sc != RTEMS_SUCCESSFUL) rtems_fatal_error_occurred (sc); } if (tty->device.lastClose) (*tty->device.lastClose)(tty->major, tty->minor, arg); if (tty->forw == NULL) { rtems_termios_ttyTail = tty->back; if ( rtems_termios_ttyTail != NULL ) { rtems_termios_ttyTail->forw = NULL; } } else { tty->forw->back = tty->back; } if (tty->back == NULL) { rtems_termios_ttyHead = tty->forw; if ( rtems_termios_ttyHead != NULL ) { rtems_termios_ttyHead->back = NULL; } } else { tty->back->forw = tty->forw; } rtems_semaphore_delete (tty->isem); rtems_semaphore_delete (tty->osem); rtems_semaphore_delete (tty->rawOutBuf.Semaphore); if ((tty->device.pollRead == NULL) || (tty->device.outputUsesInterrupts == TERMIOS_TASK_DRIVEN)) rtems_semaphore_delete (tty->rawInBuf.Semaphore); free (tty->rawInBuf.theBuf); free (tty->rawOutBuf.theBuf); free (tty->cbuf); free (tty); } rtems_semaphore_release (rtems_termios_ttyMutex); return RTEMS_SUCCESSFUL;}rtems_status_code rtems_termios_bufsize ( int cbufsize, int raw_input, int raw_output){ rtems_termios_cbufsize = cbufsize; rtems_termios_raw_input_size = raw_input; rtems_termios_raw_output_size = raw_output; return RTEMS_SUCCESSFUL;}static void termios_set_flowctrl(struct rtems_termios_tty *tty){ rtems_interrupt_level level; /* * 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_interrupt_disable(level); 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->device.write)(tty->minor, (char *)&tty->rawOutBuf.theBuf[tty->rawOutBuf.Tail],1); } /* reenable interrupts */ rtems_interrupt_enable(level); } } /* 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); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -