Linux系统中终端设备种类 

终端是一种字符型设备,有多种类型,通常使用tty 来简称各种类型的终端设备。终端特殊设备文件一般有以下几种: 串行端口终端(/dev/ttySn ) ,伪终端(/dev/pty/ ),控制终端(/dev/tty ) ,控制台终端(/dev/ttyn, /dev/console )。

1. 串行端口终端(Serial Port Terminal )是使用计算机串行端口连接的终端设备。Linux中每个串行端口都看作是一个字符设备。串行端口所对应的设备名称是/dev/tts/0 (或/dev/ttyS0 )、/dev/tts/1 (或/dev/ttyS1 )等,设备号分别是(4,0 )、(4,1 )等,分别对应于DOS 系统下的COM1 、COM2 等。

2. 伪终端(Pseudo Terminal )是成对的逻辑终端设备,例如/dev/ptyp3 和/dev/ttyp3 (在设备文件系统中分别是/dev/pty/m3 和/dev/pty/s3 ), 它们与实际物理设备并不直接相关。对于ttyp3 (s3 ),任何设计成使用一个串行端口设备的程序都可以使用该逻辑设备。但对于使用ptyp3 的程序,则需要专门设计来使用ptyp3 (m3 )逻辑设备。 如果使用telnet 远程登录,则telnet 会开始连接到设备ptyp2 (m2 )上(一个伪终端端口上)。此时一个getty 程序就应该运行在对应的ttyp2 (s2 )端口上。当telnet 从远端获取了一个字符时,该字符就会通过m2 、s2 传递给getty 程序,而getty 程序就会通过s2 、m2 和telnet 程序往网络上返回”login: ”字符串信息。登录程序与telnet 程序就通过“伪终端”进行通信,通过使用适当的软件,就可以把两个甚至多个伪终端设备连接到同一个物理串行端口上。因为只存在16个ttyp (ttyp0 —ttypf )的设备文件,就使用了象q 、r 、s 等字符来代替p 。例如,ttys8 和ptys8 就是一个伪终端设备对。目录/dev/pts 是一个类型为devpts 的文件系统,并且可以在被加载文件系统列表中看到。虽然/dev/pts 象是设备文件系统中的一项,但它其实是一种不同的文件系统。

3. 如果当前进程有控制终端(Controlling Terminal )的话,那么/dev/tty 就是当前进程的控制终端的设备特殊文件。可以使用命令”ps –ax ”来查看进程与哪个控制终端相连使用命令”tty ”可以查看它具体对应哪个实际终端设备。/dev/tty 有些类似于到实际所使用终端设备的一个联接。

4.  Linux 系统中,计算机显示器通常被称为控制台终端(Console ), 它仿真了类型为Linux 的一种终端(TERM=Linux ),并且有一些设备特殊文件与之相关联:tty0 、tty1 、tty2 等。当你在控制台上登录时,使用的是tty1 。使用Alt+[F1 —F6] 组合键时,我们就可以切换到tty2 、tty3 等上面去。tty1 –tty6 等称为虚拟终端,而tty0则是当前所使用虚拟终端的一个别名,系统所产生的信息会发送到该终端上。只有系统或超级用户root 可以向/dev/tty0进行写操作 .


终端的体系结构                带线路规程的串口驱动数据流向

tty核心:(系统实现)

tty线路规程:(自己实现,可有可无)实现规范或协议,通过注册实现。(在驱动中使用相关协议都需要注册么?USB、I2C、SPI)

tty驱动:(自己实现)

ttyS0:串口设备

tty_io.c:属于tty核心部分

n_tty.c:线路规程部分

串口驱动数据流与函数调用

串口驱动中并不像一般的字符驱动在函数指针中实现了读的操作,串口设备(Hardware层)接收到数据后产生中断,然后将数据传入到线路规程中存储起来,当用户层需要读取数据时再去线路规程中去取数据。这个过程中TTY核心将从TTY驱动收到的数据缓存到一个 tty_flip_buffer类型的结构中。该结构包含两个数据数组。从TTY设备接收到的数据被存储于第一个数组,当这个数组满时,等待数据的用户将被通知。当用户从这个数组中读数据时,任何从TTY驱动新来的数据将被存储在第2个数组。然后交替使用。

串口驱动的描述

struct uart_driver {                 //include\linux\Serial_core.h
struct module *owner;
const char *driver_name;  //驱动名
const char *dev_name;    //设备名
int major;      //主设备号
int minor;      //起始次设备号
int nr;        //设备数
struct console *cons;   /*
* these are private; the low level driver should not
* touch these; they should be initialised to NULL
*/
struct uart_state *state;
struct tty_driver *tty_driver;
};

串口驱动的注册

int uart_register_driver(struct uart_driver *uart);

UART端口描述

uart_port用于描述一个UART端口(一个串口)的地址、FIFO大小、端口类型等信息。

struct uart_port {
spinlock_t lock; /* port lock 端口锁*/
unsigned long iobase; /* in/out[bwl] IO端口基地址*/
unsigned char __iomem *membase; /* read/write[bwl]IO内存基地址 */
unsigned int (*serial_in)(struct uart_port *, int);
void (*serial_out)(struct uart_port *, int, int);
unsigned int irq; /* irq number 中断号*/
unsigned int uartclk; /* base uart clock */
unsigned int fifosize; /* tx fifo size 传输fifo大小*/
unsigned char x_char; /* xon/xoff char */
unsigned char regshift; /* reg offset shift */
unsigned char iotype; /* io access style */
unsigned char unused1; #define UPIO_PORT (0)
#define UPIO_HUB6 (1)
#define UPIO_MEM (2)
#define UPIO_MEM32 (3)
#define UPIO_AU (4) /* Au1x00 type IO */
#define UPIO_TSI (5) /* Tsi108/109 type IO */
#define UPIO_DWAPB (6) /* DesignWare APB UART */
#define UPIO_RM9000 (7) /* RM9000 type IO */ unsigned int read_status_mask; /* driver specific */
unsigned int ignore_status_mask; /* driver specific */
struct uart_info *info; /* pointer to parent info */
struct uart_icount icount; /* statistics */ struct console *cons; /* struct console, if any */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
unsigned long sysrq; /* sysrq timeout */
#endif upf_t flags; #define UPF_FOURPORT ((__force upf_t) (1 << 1))
#define UPF_SAK ((__force upf_t) (1 << 2))
#define UPF_SPD_MASK ((__force upf_t) (0x1030))
#define UPF_SPD_HI ((__force upf_t) (0x0010))
#define UPF_SPD_VHI ((__force upf_t) (0x0020))
#define UPF_SPD_CUST ((__force upf_t) (0x0030))
#define UPF_SPD_SHI ((__force upf_t) (0x1000))
#define UPF_SPD_WARP ((__force upf_t) (0x1010))
#define UPF_SKIP_TEST ((__force upf_t) (1 << 6))
#define UPF_AUTO_IRQ ((__force upf_t) (1 << 7))
#define UPF_HARDPPS_CD ((__force upf_t) (1 << 11))
#define UPF_LOW_LATENCY ((__force upf_t) (1 << 13))
#define UPF_BUGGY_UART ((__force upf_t) (1 << 14))
#define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15))
#define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))
#define UPF_CONS_FLOW ((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ ((__force upf_t) (1 << 24))
/* The exact UART type is known and should not be probed. */
#define UPF_FIXED_TYPE ((__force upf_t) (1 << 27))
#define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))
#define UPF_FIXED_PORT ((__force upf_t) (1 << 29))
#define UPF_DEAD ((__force upf_t) (1 << 30))
#define UPF_IOREMAP ((__force upf_t) (1 << 31)) #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff))
#define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY)) unsigned int mctrl; /* current modem ctrl settings */
unsigned int timeout; /* character-based timeout */
unsigned int type; /* port type */
const struct uart_ops *ops;            /*函数指针*/
unsigned int custom_divisor;
unsigned int line; /* port index */
resource_size_t mapbase; /* for ioremap */
struct device *dev; /* parent device */
unsigned char hub6; /* this should be in the 8250 driver */
unsigned char suspended;
unsigned char unused[2];
void *private_data; /* generic platform data pointer */
};

struct uart_ops定义了针对串口的一系列操作,包括发送、接收及线路设置等。

struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *);
void (*set_mctrl)(struct uart_port *, unsigned int mctrl);
unsigned int (*get_mctrl)(struct uart_port *);
void (*stop_tx)(struct uart_port *);      //停止发送
void (*start_tx)(struct uart_port *);      //开始发送
void (*send_xchar)(struct uart_port *, char ch);  //发送xchar
void (*stop_rx)(struct uart_port *);       //停止接收
void (*enable_ms)(struct uart_port *);
void (*break_ctl)(struct uart_port *, int ctl);
int (*startup)(struct uart_port *);
void (*shutdown)(struct uart_port *);
void (*flush_buffer)(struct uart_port *);
void (*set_termios)(struct uart_port *, struct ktermios *new,
struct ktermios *old);
void (*set_ldisc)(struct uart_port *);
void (*pm)(struct uart_port *, unsigned int state,
unsigned int oldstate);
int (*set_wake)(struct uart_port *, unsigned int state); /*
* Return a string describing the type of the port
*/
const char *(*type)(struct uart_port *); /*
* Release IO and memory resources used by the port.
* This includes iounmap if necessary.
*/
void (*release_port)(struct uart_port *); /*
* Request IO and memory resources used by the port.
* This includes iomapping the port if necessary.
*/
int (*request_port)(struct uart_port *);
void (*config_port)(struct uart_port *, int);
int (*verify_port)(struct uart_port *, struct serial_struct *);
int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLL
void (*poll_put_char)(struct uart_port *, unsigned char);
int (*poll_get_char)(struct uart_port *);
#endif
};

添加端口

int uart_add_one_port(struct uart_driver *drv, struct uart_port *port);

串口驱动实现操作流程

  1. 定义一个uart_driver的变量,并初始化;
  2. 使用uart_register_driver来注册这个驱动;
  3. 初始化uart_port和ops函数表;
  4. 调用uart_add_one_port()添加初始化好的uart_port。

串口驱动代码实例分析

发送和接收的原理

发送:循环buffer ->发送fifo->发送移位寄存器

接收:接收移位寄存器à接收fifo ->Flip_buf

发送的过程是:把数据写到发送fifo中,fifo把收到的数据传给发送移位寄存器(自动的,非driver控制),然后每个时钟脉冲往串口线上写一bit数据。

接收的过程是:接收移位寄存器收到数据,发送给接收fifo,接收fifo事先设置好了触发门限(寄存器中设置),当里面的数据量超过门限时就会触发一个中断,调用驱动中的中断处理函数,把数据写到flip_buf中。

基于2.4内核的代码实现

2.4与2.6内核串口驱动的唯一区别是2.4中port直接作为struct uart_driver的成员,但是2.6中不是直接作为其成员,而是使用uart_add_one_port()函数将uart_driver与port关联起来。

模块初始化函数

static int __init s3c2410uart_init(void)
{
return uart_register_driver(&s3c2410_reg);
}

使用uart_register_driver注册串口驱动。注册前需要实现串口驱动的结构s3c2410_reg;如下

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,
port: s3c2410_ports,  //最重要的成员
cons: S3C2410_CONSOLE,
}; static struct uart_port s3c2410_ports[UART_NR] = {
{
iobase: (unsigned long)(UART0_CTL_BASE), //IO基地址
iotype: SERIAL_IO_PORT,           //类型IO
irq: IRQ_RXD0,               //中断
uartclk: 130252800,              //时钟
fifosize: 16,                  //fifo大小
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,
};

部分操作函数实现

阻止发送函数uart_stop_tx 

static void s3c2410uart_stop_tx(struct uart_port *port, u_int from_tty)
{
  disable_irq(TX_IRQ(port));
}
//停止发送的功能,其内部的函数disable_irq是停止中断的功能 ,发送数据是通过中断来完成的,关闭中断也就关闭了发送。

发送使能函数uart_start_tx

static void s3c2410uart_start_tx(struct uart_port *port, u_int nonempty, u_int from_tty)
{
enable_irq(TX_IRQ(port));
}
//与上面的过程类似,就是一个相反的过程

阻止接收函数uart_stop_rx 

static void s3c2410uart_stop_rx(struct uart_port *port)
{
disable_irq(RX_IRQ(port));
}
//阻止接收的原理与阻止发送的原理是一样的

发送缓冲空判断函数uart_tx_empty

static u_int s3c2410uart_tx_empty(struct uart_port *port)
{
return (UART_UTRSTAT(port) & UTRSTAT_TR_EMP ? 0 : TIOCSER_TEMT);
} //如果发送缓冲为空则返回0,否则返回1。
//在寄存器UTRSTATn [2]标志了缓存是否为空,UART_UTRSTAT(port) & UTRSTAT_TR_EMP便是对应着这一位的值。

获取控制信息函数uart_get_mctrl 

static u_int s3c2410uart_get_mctrl(struct uart_port *port)
{
  return (TIOCM_CTS | TIOCM_DSR | TIOCM_CAR);
}
//获得控制信息, TIOCM_CTS ,TIOCM_DSR 和TIOCM_CAR,这几个宏代表串口的控制信息, 分别是clear to send,data set ready和data carrier detect(详见Serial Programming Guide for POSIX Operating Systems)

接收中断函数uart_rx_interrupt 

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--)  //一个字节一个字节的发送,直达全部字节(每次最多处理man_count个字节)发送完毕
  {
    if (tty->flip.count >= TTY_FLIPBUF_SIZE)   //交替使用buffer
    {
      tty->flip.tqueue.routine((void *) 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;          //将从串口接收到的数据(一个字节)存到flip buffer中
    *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;
} /**********************

功能:主要是是while大循环,首先看循环判断条件status & UTRSTAT_RX_RDY,前面有status = UART_UTRSTAT(port),查2410的datasheet, status & UTRSTAT_RX_RDY这个位是判断接收buffer内是否还有有效数据?按道理一次中断只是把接收的fifobuffer中的数据放到flipbuffer中去,接收的fifo的中断门限是4-12字节,进行一次接收往往要中断好多次,这样中断开销比较大,所以在while的循环条件中判断一下是否还有接收的有效数据,如果有,就继续在中断程序中继续接收,当然,永远都在接收中断中(如果一直有数据要接收)也不合适,所以while循环还有计数,最多循环256次。

在循环中,首先是要判断一下接收数据用的flip-buffer是不是已经满了, if (tty->flip.count >= TTY_FLIPBUF_SIZE)如果满了,就要跳到另一个buffer上去, tty->flip.tqueue.routine((void *) tty)是用来实现跳到另一个buffer上的功能,然后把收到的数据写到flip-buffer中,相应的状态,统计数据都要改,接着再来while 循环,循环结束后就要调用tty_flip_buffer_push(tty)来让用户把存在缓冲里的数据取走,接收一次都要把缓存清空。

*********************/

发送中断函数uart_tx_interrupt 

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) {             //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   //循环buffer是否没数据要发送
                          || info->tty->hw_stopped) {
    s3c2410uart_stop_tx(info->port, 0);                 //没数据的话关闭发送,否则可能产生中断
    return;
  }
  count = port->fifosize >> 1;                     //否则 16/2,即每次操作两组fifo中的一组   do {
    UART_UTXH(port) = info->xmit.buf[info->xmit.tail];
    info->xmit.tail = (info->xmit.tail + 1) & (UART_XMIT_SIZE - 1);
    port->icount.tx++;
    if (info->xmit.head == info->xmit.tail)
      break;
  } while (--count > 0);
  if (CIRC_CNT(info->xmit.head, info->xmit.tail, UART_XMIT_SIZE) < WAKEUP_CHARS)
    uart_event(info, EVT_WRITE_WAKEUP);           //通知tty core和上层,fifo中有空间可以存储数据了
  if (info->xmit.head == info->xmit.tail)
    s3c2410uart_stop_tx(info->port, 0);
} /*

(1) 首先查看port中的x_char是不是为0,不为0则把x_char发送出去。x_char是xon/xoff的意思,每发一个字节时在开始前先发xon信号,在结束时发xoff。

(2) 如果x_char没有被设置,再看环形缓冲区是否为空,或者info->tty->stopped 和 info->tty->hw_stopped 两个位是不是为1,如果这些条件成立的话,就停止发送。Tty->stop指示tty设备是否停止,tty->hw_stop指示tty设备的硬件是否停止了,以上两个位都可以通过ttydriver来设定,否则的话说明有数据要发送。

(3) 如果以上条件都通过了,就利用一个while循环正式发送数据了,从环形缓冲尾巴上取一个数赋给UART_UTXH(port)(发送FIFO), UART_UTXH(port) = info->xmit.buf[info->xmit.tail],这条语句就是把数据送到发送FIFO中,然后计数++,循环一共进行fifosize/2次,也就是一次只能发送8 byte。

(4)循环传送完一次后,再查看缓冲器里还剩余多少数据,如果少于WAKEUP_CHARS(256)的话,就执行uart_event(info, 0),告诉TTY核心,可以接受更多数据了。这里可以看出,tty_driver和tty_core之间的层次,tty_driver可以知道缓冲空还是满,但是它没有权力让发送数据过来,它只能是通知tty_core,让它来处理。

(5) 最后再察看一下环形寄存器,如果serial core 没有发送来更多的数据,就关闭发送。

*/

  

出错中断函数uart_err_interrupt

static void s3c2410uart_err_interrupt(int irq, void *dev_id,
                       struct pt_regs *reg) {
  struct uart_info *info = dev_id;
  struct uart_port *port = info->port;   struct tty_struct *tty = info->tty;
  unsigned char err = UART_UERSTAT(port) & UART_ERR_MASK;
  unsigned int ch, flg;
  ch = UART_URXH(port);
  if (!(err & (UERSTAT_BRK | UERSTAT_FRAME |
      UERSTAT_PARITY | UERSTAT_OVERRUN)))
    return;
  if (err & UERSTAT_BRK)
    port->icount.brk++;
  if (err & UERSTAT_FRAME)
    port->icount.frame++;
  if (err & UERSTAT_PARITY)
    port->icount.parity++;
  if (err & UERSTAT_OVERRUN)
    port->icount.overrun++;
  err &= port->read_status_mask;
  if (err & UERSTAT_PARITY)
    flg = TTY_PARITY;
  else if (err & UERSTAT_FRAME)
    flg = TTY_FRAME;
  else
    flg = TTY_NORMAL;
  if (err & UERSTAT_OVERRUN) {
    *tty->flip.char_buf_ptr = ch;
    *tty->flip.flag_buf_ptr = flg;
    tty->flip.flag_buf_ptr++;
    tty->flip.char_buf_ptr++;
    tty->flip.count++;
    if (tty->flip.count < TTY_FLIPBUF_SIZE) {
      ch = 0;
      flg = TTY_OVERRUN;
    }
  }
  *tty->flip.flag_buf_ptr++ = flg;
  *tty->flip.char_buf_ptr++ = ch;
  tty->flip.count++;
}
#endif /*

首先err = UART_UERSTAT(port) & UART_ERR_MASK确定了err的值,err的值是从是从UART Error Status Register读到的,该erstate只用了四位,所以用UART_ERR_MASK把高四位掩掉,然后测试低四位中哪个位被置1了,从而判断错误种类UERSTAT_BRK/FRAME/PARITY/OVERRUN 分别代表1000/0100/0010/0001 ,判断出错误种类再进行相应的中断计数,然后再根据不同的err给flg设上不同的值,有

#define TTY_NORMAL 0

#define TTY_BREAK 1

#define TTY_FRAME 2

#define TTY_PARITY 3

#define TTY_OVERRUN 4

*/

  

初始化函数uart_startup

static int s3c2410uart_startup(struct uart_port *port, struct uart_info *info)
{
  int ret, flags;
  u_int ucon;
  ret = request_irq(RX_IRQ(port), s3c2410uart_rx_interrupt, SA_INTERRUPT,
    "serial_s3c2410_rx", info);
  if (ret) goto rx_failed;
  ret = request_irq(TX_IRQ(port), s3c2410uart_tx_interrupt, SA_INTERRUPT,
    "serial_s3c2410_tx", info);
  if (ret) goto tx_failed;
#ifdef CONFIG_USE_ERR_IRQ
  ret = request_irq(ERR_IRQ(port), s3c2410uart_err_interrupt, SA_INTERRUPT,
    "serial_s3c2410_err", info);
  if (ret) goto err_failed;
#endif
  ucon = (UCON_TX_INT_LVL | UCON_RX_INT_LVL |
       UCON_TX_INT | UCON_RX_INT | UCON_RX_TIMEOUT);
#if defined(CONFIG_IRDA) || defined(CONFIG_IRDA_MODULE)
  ULCON2 |= ULCON_IR | ULCON_PAR_NONE | ULCON_WL8 | ULCON_ONE_STOP;
#endif   save_flags(flags);
  cli();
  UART_UCON(port) = ucon;
  sti();
  restore_flags(flags);
  return 0;
#ifdef CONFIG_USE_ERR_IRQ
  err_failed:
  free_irq(TX_IRQ(port), info);
#endif
  tx_failed:
  free_irq(RX_IRQ(port), info);
  rx_failed:
  return ret;
} /*

如果使用了函数open(ttyS0),那么最后调用的实现open功能的就是这个函数,它打开ttyS0。

1:利用request_irq()申请发送,接收,错误三个中断,如果失败,就要释放调已经申请的全部资源

2:设置UART Control Register

*/

函数uart_change_speed

static void s3c2410uart_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u                      _int quot)
{
  u_int ulcon, ufcon;   int flags;
  ufcon = UART_UFCON(port);
  switch (cflag & CSIZE) {
    case CS5: ulcon = ULCON_WL5; break;
    case CS6: ulcon = ULCON_WL6; break;
    case CS7: ulcon = ULCON_WL7; break;
    default: ulcon = ULCON_WL8; break;
  }
  if (cflag & CSTOPB)
  ulcon |= ULCON_STOP;
  if (cflag & PARENB) {
    if (!(cflag & PARODD))
    ulcon |= ULCON_PAR_EVEN;
  }
  if (port->fifosize > 1)
    ufcon |= UFCON_FIFO_EN;
  port->read_status_mask = UERSTAT_OVERRUN;
  if (iflag & INPCK)
    port->read_status_mask |= UERSTAT_PARITY | UERSTAT_FRAME;
    port->ignore_status_mask = 0;
  if (iflag & IGNPAR)
    port->ignore_status_mask |= UERSTAT_FRAME | UERSTAT_PARITY;
  if (iflag & IGNBRK) {
    if (iflag & IGNPAR)
      port->ignore_status_mask |= UERSTAT_OVERRUN;
  }
  quot -= 1;
  save_flags(flags);
  cli();
  UART_UFCON(port) = ufcon;
  UART_ULCON(port) = (UART_ULCON(port) & ~(ULCON_PAR | ULCON_WL)) | ulcon;
  UART_UBRDIV(port) = quot;
  sti();
  restore_flags(flags);
} /*

1:

UBRDIVn=(int)(CLK/(bps*16))-1

quot=(CLK / (baudrate x 16) ) (CLK为PCLK或UCLK,baudrate的单位是bps

(1):首先看一下cflag的cs位,同CS5/6/7比较,然后设置ulcon,接下来的几个if也是将ulcon根据cflag的设置进行一下设置,设置了停止位,校验位。

(2):如果port中设置了fifosize,就把UFCON(物理地址0x50000008)的第0位设为1。

*/

  

控制台

注册控制台 

void __init s3c2410_console_init(void)
{
  register_console(&s3c2410_cons);
} static struct console s3c2410_cons = {
name: "ttyS",
write: s3c2410_console_write,
device: s3c2410_console_device,
wait_key: s3c2410_console_wait_key,
setup: s3c2410_console_setup,
flags: CON_PRINTBUFFER,
index: -1,
};

  

函数console_write

static void s3c2410_console_write(struct console *co, const char *s, u_int count)
{
  int i;
  struct uart_port *port = s3c2410_ports + co->index;
  for (i = 0; i < count; i++) {
    while (!(UART_UTRSTAT(port) & UTRSTAT_TX_EMP));
    UART_UTXH(port) = s[i];
    if (s[i] == '\n') {
      while (!(UART_UTRSTAT(port) & UTRSTAT_TX_EMP));
      UART_UTXH(port) = '\r';
    }
  }
} /*

通过串口往外发送数据

for循环count次,每次发送一个字符,当发送缓冲寄存器为空时,就往里写一个字符,如果写的数据是回车加换行,就要再写一个换行符

*/

  

函数console_waitkey

static int s3c2410_console_wait_key(struct console *co)
{
int c;
struct uart_port *port = s3c2410_ports + co->index;
while (!(UART_UTRSTAT(port) & UTRSTAT_RX_RDY));
c = UART_URXH(port); return c;
} /*

该函数在while循环中等待接收数据,一旦接收缓冲器中有有效数据,该函数立即返回,返回值为接收到的一个字符

*/

  

函数console_device

static kdev_t s3c2410_console_device(struct console *co)
{
return MKDEV(SERIAL_S3C2410_MAJOR, MINOR_START + co->index);
} /*

通过主,次设备号返回kdev_t结构

*/

  

设置函数console_setup 

static int __init s3c2410_console_setup(struct console *co, char *options)
{
  struct uart_port *port;
  int baud = 115200;
  int bits = 8;
  int parity = 'n';
  int flow = 'n';
  port = uart_get_console(s3c2410_ports, UART_NR, co);
  if (options)
    uart_parse_options(options, &baud, &parity, &bits, &flow);
  return uart_set_options(port, co, baud, parity, bits, flow);
} /*

这个函数就是设置控制台(console)的状态,里面主要有三个函数

(1)uart_get_console (struct uart_port *ports, int nr, struct console *co)

该函数检查是否co->index是一个无效的index number,返回指向该index number 对应的uart_port struct的指针

(2)uart_parse_options (options, &baud, &parity, &bits, &flow)

如果option被设置了,那么就要调用该函数,该函数解析options,options来自上一层函数的参数,它是一个字符串,应该包含baudrate,parity,bits,flow这些信息。

(3)uart_set_options( port, co, baud, parity, bits, flow)

针对以上给定的信息,对console的cflag进行设置.还要调用一下ops中的change_speed对baud rate进行实际的设置(物理),成功地话return 0

*/

 


终端控制

Linux提供了命令或相关函数对终端进行控制,已达到用户对输出显示或终端的特定要求。

终端控制命令

$ stty #显示终端的相关信息

stty命令可以直接修改和查询终端驱动程序的设置。

终端控制函数

获取文件描述符是否是某个终端设备

#include <unistd.h>
int isatty(int filedes) ;
//返回:若为终端设备则为1(真),否则为0(假)
char *ttyname(int filedes) ;
//返回:指向终端路径名的指针,若出错则为NULL
                         //错误信息:EBADF:非法文件描述符 ENOTTY:该文件描述符非终端描述符

函数tcgetattr和tcsetattr可以获得或设置termios。

struct termios{
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lfalg;
cc_t c_cc[NCCS];  //控制字符,用于保存终端驱动程序中的特殊字符。
};
#include <termios.h>
int tcgetattr(int filedes, struct termios* termptr) ;
int tcsetattr(int filedes, int opt, const struct termios* termptr) ;
//两个函数返回:若成功则为0,若出错则为- 1

终端标志

 

To be continue...

Linux系统编程@终端IO的更多相关文章

  1. Linux系统编程--文件IO操作

    Linux思想即,Linux系统下一切皆文件. 一.对文件操作的几个函数 1.打开文件open函数 int open(const char *path, int oflags); int open(c ...

  2. linux系统编程--文件IO

    系统调用 什么是系统调用: 由操作系统实现并提供给外部应用程序的编程接口.(Application Programming Interface,API).是应用程序同系统之间数据交互的桥梁. C标准函 ...

  3. linux系统编程:IO读写过程的原子性操作实验

    所谓原子性操作指的是:内核保证某系统调用中的所有步骤(操作)作为独立操作而一次性加以执行,其间不会被其他进程或线程所中断. 举个通俗点的例子:你和女朋友OOXX的时候,突然来了个电话,势必会打断你们高 ...

  4. linux系统编程之文件与io(一)

    经过了漫长的学习,C语言相关的的基础知识算是告一段落了,这也是尝试用写博客的形式来学习c语言,回过头来看,虽说可能写的内容有些比较简单,但是个人感觉是有史起来学习最踏实的一次,因为里面的每个实验都是自 ...

  5. linux系统编程之文件与io(五)

    上一节中已经学习了文件描述符的复制,复制方法有三种,其中最后一种fcntl还并未使用到,关于这个函数,不光只有复制文件描述符的功能,还有其它一些用法,本节就对其进行一一剖析: fcntl常用操作: 这 ...

  6. 读书笔记之Linux系统编程与深入理解Linux内核

    前言 本人再看深入理解Linux内核的时候发现比较难懂,看了Linux系统编程一说后,觉得Linux系统编程还是简单易懂些,并且两本书都是讲Linux比较底层的东西,只不过侧重点不同,本文就以Linu ...

  7. Linux 系统编程 学习:01-进程的有关概念 与 创建、回收

    Linux 系统编程 学习:01-进程的有关概念 与 创建.回收 背景 上一讲介绍了有关系统编程的概念.这一讲,我们针对 进程 开展学习. 概念 进程的身份证(PID) 每一个进程都有一个唯一的身份证 ...

  8. Linux系统编程【2】——编写who命令

    学到的知识点 通过实现who命令,学到了: 1.使用man命令寻找相关信息 2.基于文件编程 3.体会到c库函数与系统调用的不同 4.加深对缓冲技术的理解 who命令的作用 who命令的使用 在控制终 ...

  9. Linux系统编程温故知新系列 --- 01

    1.大端法与小端法 大端法:按照从最高有效字节到最低有效字节的顺序存储,称为大端法 小端法:按照从最低有效字节到最高有效字节的顺序存储,称为小端法 网际协议使用大端字节序来传送TCP分节中的多字节整数 ...

随机推荐

  1. elementoryOS / ubuntu U盘启动问题的解决

    具体现象: 进入U盘启动后,停顿在"start booting from usb device..."不动. 解决方法:  将syslinux文件夹下的syslinux.cfg中的 ...

  2. [C/C++]C++声明

    [注]本文是Declarations的翻译和注解版. https://msdn.microsoft.com/en-us/library/f432x8c6.aspx 1.声明: 我们通过声明往C++程序 ...

  3. C# Inject

    using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Di ...

  4. 《java作业》

    /* 2.编写一个类,该类有一个方法public int f(int a,int b), 该方法返回a和b的最大公约数.然后再编写一个该类的子类, 要求子类重写方法f,而且重写的方法将返回a和b的最小 ...

  5. Ubuntu 14.10 下查看系统硬件信息(实例详解)

    linux查看系统的硬件信息,并不像windows那么直观,这里我罗列了查看系统信息的实用命令,并做了分类,实例解说. cpu lscpu命令,查看的是cpu的统计信息. blue@blue-pc:~ ...

  6. yii2归档安装

    1.http://www.yiiframework.com/download/ 下载文件 2.如果inint.bat文件一闪而过,没有提示是开发还是生产环境 用编辑器(phpstorm)打开文件在对应 ...

  7. office2010安装Microsoft Office Document Imaging (MODI) 图解

    office2010安装Microsoft Office Document Imaging (MODI) 图解     Microsoft Office 2010 中删除了 Microsoft Off ...

  8. The Blocks Problem

    Description Many areas of Computer Science use simple, abstract domains for both analytical and empi ...

  9. 钱币兑换问题_完全背包&&拆分&&母函数

    ps:原来用新浪,可是代码的排版不是很好,所以用博客园啦,先容许我把从八月份开始的代码搬过来,从这里重新出发,希望这里可以一直见证我的成长. Time Limit: 2000/1000 MS (Jav ...

  10. HDU 5001

    http://acm.hdu.edu.cn/showproblem.php?pid=5001 每次去掉要算的点,求出到达其他点的概率,就是不能到达这个点的概率 开始想去算到达这个点的概率,再去减,不过 ...