串口驱动程序设计

终端控制台体系

串口之前要知道终端控制台,在Linux中,tty(终端)是一类字符设备的统称,包括3种类型: 控制台、串口和伪终端。

控制台:内核信息打印到的地方,printk打印的地方,控制台可以指定,通过console=ttys0指定串口0终端为控制台,是虚拟的要绑定真实的设备,比如串口、显示器。

伪终端:成对出现的,通常用来做远程通信,类似管道。

串口驱动程序设计_第1张图片

终端体系:包含:tty coretty线路规程(tty line discipline),tty驱动。tty core:发送数据时从用户拿到数据交给线路规程,线路规程处理完后把数据还给tty coretty core再交给tty driverdriver同硬件通信把数据发走,从硬件接收到的数据向上通过tty 驱动,进入线路规程,再进入tty core,最后被用户获取。

串口驱动程序设计_第2张图片

当通过串口发送数据时,操作的设备文件是/dev/ttys0,往设备文件中写入数据,数据沿着红线往下走首先进入tty_io.c(代表tty core),tty core 把数据交给线路规程(n_tty.c),他把数据按照某些规定协议(PPP点对点,蓝牙。。。。。)处理数据,处理完后把数据较还给tty_io.c(tty core) tty core 再交给tty driver (serial.c),通过硬件发送走。

串口驱动程序设计_第3张图片

字符设备数据的read()是系统调用一直往下达到设备的驱动程序里面,然后把数据返回给用户空间,tty设备是不管有无read()的需求,只要受到数据就把数据往上传,传到tty_flip_buffer类型结构把数据存起来,当用户需要数据,仅仅从receive_buf()里取。

Linux内核使用uart_driver描述串口驱动,串口驱动属于字符设备。

struct uart_driver {

struct module *owner;

const char *driver_name; 

const char *dev_name;

int  major;

int  minor;

int  nr;

struct console *cons;

struct uart_state *state;

struct tty_driver *tty_driver;

};

int uart_register_driver (struct uart_driver *drv) 注册串口驱动

uart_port用于描述一个串口。

struct uart_port {

spinlock_t lock //端口锁

unsigned int iobase ; //I/O端口基地址

unsigned char _iomem *membase ; //IO内存基地址

unsigned int irq ; //中断号

unsigned char fifosize ; //传输fifo大小

const struct uart_ops *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);

void (*stop_rx)(struct uart_port *);

..........................................................

};

int uart_add_one_port (struct uart_driver *drv,  struct uart_port *port )添加一个串口,通过第一个参数drv同驱动建立起联系。

串口驱动设计:1:定义uart_driver,并初始化。2:使用uart_register注册驱动。3:初始化uart_port和他附带的ops函数表。4:调用uart_add_one_port把初始化的端口添加到系统。

数据的发送:从循环buffer取数据------>(驱动完成)放到发送fifo----->(硬件)发送移位寄存器

接收:接收移位寄存器------>(硬件)接收fifo---------->(驱动)filp_buf

uart line control register串口数据的格式 (停止位、奇偶校验位、数据长度)

串口驱动程序设计_第4张图片

uart control registerDMA,中断触发等等。

串口驱动程序设计_第5张图片

uart fifo control register2440数据的收和发都有16个字节的fifo,什么时候产生中断在这里设置,Rx FIFO Trigger Level(接收中断: 指定收到4816.....个字节产生接收中断。

Tx FIFO Trigger Level(发送中断):设置发送FIFO低于16128......个字节产生发送中断。

先产生中断告诉你可以再发送一些数据,然后再发送。

 

UART TX/RX Status Register :判断有无数据等待发送

串口驱动程序设计_第6张图片

函数介绍

模块初始化函数:

static int __init s3c2410uart_init(void)

{

return uart_register_driver(&s3c2410_reg);

}

使用uart_register_driver注册串口驱动。

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),

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,

};

 停止发送函数 uart_stop_tx 

 statuc void s3c2410uart_stop_tx (struct uart_port *port , u_int from_tty) {

disable_irq(TX_IRQ(port)) ;  //关闭发送中断,数据的发送依靠发送中断

}

发送使能函数uart_stat_tx (struct uart_port *port  , u_int noempty, u_int (from_tty)) {

enable_irq(TX_IRQ(port)) ;

}

阻止接收中断uart_stop_tx

statuc void uart_stop_tx (struct uart_port *port) {

disable_irq(RX_IRQ(port)) ;

}

发送缓冲fifo空判断函数uart_tx_empty

static u_int s3c2410uart_tx_empty (struct uart_port *port ) {

return  (UART_UTRSTAT(uart_port)  &  UTRSTAT_TR_EMP ?  0:

TIOCSER_TEMT) ; //读发送fifo的状态判空

}

接收是收到的数据达到触发水平以后,产生接收中断,接收是把数据送进flip_buffer

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--){ 
  if (tty->flip.count >= TTY_FLIPBUF_SIZE) { /把数据送进flip_buf之前看flip_buf有无数据空间,tty->flip.count :已经存放的数据量,当没有空间时。
  tty->flip.tqueue.routine((void *) tty); //数据满时,切换到另一个flip_buffer。
  
  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); 	//把flip_buffer数据放入tty_buffer供用户取走
  return; }	//一次中断尽可能处理多的字符

发送在中断产生以后进行的,有一个中断处理函数实现。

发送中断函数uart_tx_interrupt 

tatic 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 = port->fifosize >> 1;   //发送接收fifosize大小为16字节
  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); 	//一次最多循环8个字符
  if (CIRC_CNT(info->xmit.head, info->xmit.tail, //计算循环buffer里剩余的数据量
  UART_XMIT_SIZE) < WAKEUP_CHARS) 
  uart_event(info, EVT_WRITE_WAKEUP); //小于时,告诉上层还可以写数据量
  if (info->xmit.head == info->xmit.tail) 
  s3c2410uart_stop_tx(info->port, 0); 
  } 
  
  
  do { 
  UART_UTXH(port) = info->xmit.buf[info->xmit.tail]; 
  //从循环buffer里取字符送入相应寄存器
  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); 
  if (info->xmit.head == info->xmit.tail) 
  s3c2410uart_stop_tx(info->port, 0); 
  } 





你可能感兴趣的:(Linux,设备驱动程序)