终端学习
终端(tty):
在LINUX中,终端(TTY)是一类字符设备的统称,包含了三种类型:控制台,串口等物理串口和伪终端。
控制台:
供内核使用的终端为控制台,控制台在linux启动时,通过命令console=…制定,如果没有指定控制台,系统会把第一个注册的终端作为控制台。
控制台有如下的特点:
1. 控制台是一个虚拟的终端,它必须映射到真正的终端上。比如映射到串口上。
2. 控制台可以简答的理解为printk输出的地方。
3. 控制台是个只能输出的设备,功能很简单,只能在内核中访问。
伪终端:
伪终端设备是一种特殊的终端设备,由主-从两个成对设备构成,当打开主设备时,对应的从设备随之打开,形成连接状态。
伪终端设备常用于远程登录服务器来建立网络和终端的关联。比如telnet。
终端的体系:
在Linux中,TTY体系分为:TTY核心,TTY线路规程,TTY驱动3部分。如下图:
TTY核心从用户获取要发送给TTY设备的数据,然后把数据传递给TTY线路规程, 它对数据进行处理后,负责把数据传递到TTY驱动程序,TTY驱动程序负责格式化数据,并通过硬件发送出去。
从硬件收到的数据向上通过TTY驱动, 进入TTY线路规程, 再进入TTY核心, 最后被用户获取。TTY驱动可以直接和TTY核心通讯, 但是通常TTY线路规程会修改在两者之间传送的数据。TTY驱动不能直接和线路规程通信,甚至不知道它的存在,线路规程的工作是格式化从用户或者硬件收到的数据. 这种格式化常常实现为一个协议, 如PPP或Bluetooth。
例如,串口的终端体系如下:可以看出来数据的流向是:tty核心-tty线程规划-tty核心-tty驱动。
系统调用的调用方式如下:
串口总结:
串口的使用方式:
串口有两种使用方式:1.作为终端使用。2.作为控制台使用
寄存器总结:(下面的不全,因为以前用汇编写过相关程序,所以下面大致说一下)
UART Line Control Register:对串口参数的设置,比如数据位的长度。
UART Control Register:一些控制参数,比如中断控制等。
UART FIFO Control Register:主要是对FIFO的控制,如FIFO使能,接受FIFO触发中断的阈值。
UART TX/RX Status Register:接受和发送寄存器的状态。用于非FIFO状态。
UART FIFO Status Register:记录FIFO的状态。用于非FIFO状态。
串口的结构如下,发送缓冲和接受缓冲均为64字节,当开启FIFO模式的时候,用到其中的一部分,当开启非FIFO模式的时候,只用到其中的一个字节。
外部中断边沿触发和电平触发的区别:
边沿触发:如果是下降沿触发,当从高电平至低电平转变时候,触发产生,低电平保持多久都只产生一次。
点评触发:如果是低电平触发,那么在低电平时间中断一直有效。
串口的传送方式:
单工方式:数据始终是按一个方向传送的。
半双工方式:数据可以双向传输,但是在任意一个时刻只能有一个设备发送,一个设备接受。
全双工方式:允许双方同时进行发送和接受。即一个设备发送的同时也能接受。
代码注释:
//串口结构体
static struct uart_driver s3c2410_reg = {
owner: THIS_MODULE,
normal_major: SERIAL_S3C2410_MAJOR,
normal_name: "ttyS%d",
callout_name: "cua%d",
normal_driver: &normal,
callout_major: CALLOUT_S3C2410_MAJOR,
callout_driver: &callout,
table: s3c2410_table,
termios: s3c2410_termios,
termios_locked: s3c2410_termios_locked,
minor: MINOR_START,
nr: UART_NR,
//主要是这里,s3c2410_ports中的每一个元组代表一个串口
port: s3c2410_ports,
cons: S3C2410_CONSOLE,
};
static struct uart_port s3c2410_ports[UART_NR] = {
{
iobase: (unsigned long)(UART0_CTL_BASE),
iotype: SERIAL_IO_PORT,
irq: IRQ_RXD0,
uartclk: 130252800,
fifosize: 16,
//这里是系统调用集合
ops: &s3c2410_pops,
type: PORT_S3C2410,
flags: ASYNC_BOOT_AUTOCONF,
}
。。。。。。 。。。。。。。 。。。。。。。
};
static struct uart_ops s3c2410_pops = {
//这下面就是要实现的函数指针
//发送缓冲为空判断函数
tx_empty: s3c2410uart_tx_empty,
set_mctrl: s3c2410uart_set_mctrl,
//获取控制信息函数
get_mctrl: s3c2410uart_get_mctrl,
//发送失能函数
stop_tx: s3c2410uart_stop_tx,
//发送使能函数
start_tx: s3c2410uart_start_tx,
//接受失能函数
stop_rx: s3c2410uart_stop_rx,
enable_ms: s3c2410uart_enable_ms,
break_ctl: s3c2410uart_break_ctl,
startup: s3c2410uart_startup,
shutdown: s3c2410uart_shutdown,
change_speed: s3c2410uart_change_speed,
type: s3c2410uart_type,
config_port: s3c2410uart_config_port,
release_port: s3c2410uart_release_port,
request_port: s3c2410uart_request_port,
};
//发送失能函数
static void s3c2410uart_stop_tx(struct uart_port *port, u_int from_tty)
{
//因为是依靠中断来发送,所以,关闭中断就是停止发送了。同理,发送使能函数
//就是开启中断。而接受使能函数和接受失能函数也是通过开关中断来完成的。
disable_irq(TX_IRQ(port));
}
//发送中断函数
static void s3c2410uart_tx_interrupt(int irq, void *dev_id, struct pt_regs *reg) {
struct uart_info *info = dev_id;
struct uart_port *port = info->port;
int count;
if (port->x_char) {
UART_UTXH(port) = port->x_char;
port->icount.tx++;
port->x_char = 0;
return;
}
//看是否满足停止发送数据的条件,如果满足就失能发送中断
if (info->xmit.head == info->xmit.tail || info->tty->stopped || info->tty->hw_stopped) {
s3c2410uart_stop_tx(info->port, 0);
return;
}
//count代表一次最多发送的字节数,一次最多发送FIFO的一半
count = port->fifosize >> 1
do {
//发送一字节
UART_UTXH(port) = info->xmit.buf[info->xmit.tail];
info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
//查看是否将tty核心缓冲区中所有的数据都发送完了,如果已经都发送完了,则返回
if (info->xmit.head == info->xmit.tail)
break;
} while (--count > 0);
//如果满足条件 告诉tty核心可以继续发送数据
if (CIRC_CNT(info->xmit.head,info->xmit.tail,UART_XMIT_SIZE)<WAKEUP_CHARS)
uart_event(info, EVT_WRITE_WAKEUP);
//如果已经将所有的数据都发送完了,则失能发送中断。
if (info->xmit.head == info->xmit.tail)
s3c2410uart_stop_tx(info->port, 0);
}
//接收中断函数
static void s3c2410uart_rx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct uart_info *info = dev_id;
struct tty_struct *tty = info->tty;
unsigned int status, ch, max_count = 256;
struct uart_port *port = info->port;
//获取当前状态
status = UART_UTRSTAT(port);
while ((status & UTRSTAT_RX_RDY) && max_count--)
{
//如果当前tty核心的缓冲已经满了,就没办法继续向缓冲中写入数据
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
{
//切换到另一个缓冲
tty->flip.tqueue.routine((void *) tty);
//如果tty核心中另一个缓冲也已经满了,则无法继续接收数据,打印出错退出
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
{
printk(KERN_WARNING "TTY_DONT_FLIP set\n");
return;
}
}
//接收一字节
ch = UART_URXH(port);
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = TTY_NORMAL;
port->icount.rx++;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
//再次获取当前状态,为下一次循环做准备
status = UART_UTRSTAT(port);
}
//通过系统调用,传送给线程规划层
tty_flip_buffer_push(tty);
return;
}