终端学习

终端(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驱动不能直接和线路规程通信,甚至不知道它的存在,线路规程的工作是格式化从用户或者硬件收到的数据这种格式化常常实现为一个协议PPPBluetooth

 

 

 

 

例如,串口的终端体系如下:可以看出来数据的流向是: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 "); 

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; 


 





原文地址:https://www.cnblogs.com/dchipnau/p/5255324.html