了解UART串口驱动

来源:互联网 发布:淘宝曝光量是什么意思 编辑:程序博客网 时间:2024/05/02 02:18

Linux中,UART属于TTY设备三大类别中的串口,所以UART驱动完全遵循TTY驱动的架构。但事实上,Linux内核在TTY驱动框架下又封装一层UART驱动。内核中用uart_driver来表示一个UART驱动。因此在学习串口驱动前需要了解两个基本的框架结构,tty框架和uart框架。

 

首先看看tty框架:

 

最上面的用户空间会有很多对底层硬件的操作,像readwrite等。用户空间主要是通过设备文件同tty_core交互,tty_core根据用空间操作的类型再选择跟line disciplinetty_driver也就是serial_core交互,例如设置硬件的ioctl指令就直接交给serial_core处理。Readwrite操作就会交给line discipline处理。Line discipline是线路规程的意思。正如它的名字一样,它表示的是这条终端”线程”的输入与输出规范设置,主要用来进行输入/输出数据的预处理。处理之后,就会将数据交给serial_core,最后serial_core会调用底层的操作。

 

下面来看看uart框架图:

 

uart_driver用来表示串口设备驱动(统称),uart_state用来表示一个具体的串口设备,例如:/dev/ttyS0  /dev/ttyS1 每个设备节点是对应一个具体硬件的(uart_state), uart_port用来表示设备的具体信息,包括设备的控制和状态信息。ttytty_port)是从tty核心层的角度来表示一个串口设备,其中就封装了tty核心层要用到的数据结构tty_structxmit是数据发送缓冲区,用来接收上层用户传递过来的数据。

 

uart驱动注册时(uart_register_driver),会将uart设备(uart_driver)注册进tty(后面再来分析具体代码),同时将tty_driver的操作集设为了uart_ops,使得从用户空间下来的操作可以找到正确的serial_core的操作函数。

 

接下来简单介绍下串口的一次工作:

 

当用户打开串口设备,向串口写入数据时调用write()write()通过系统调用找到file_operations里的 .write函数指针,充当我们 .write的函数是 tty_write(),(PATH:/driver/tty/tty_io.c

 

static const struct file_operations tty_fops= {

.llseek= no_llseek,

.read= tty_read,

.write=tty_write,

.poll= tty_poll,

.unlocked_ioctl= tty_ioctl,

.compat_ioctl= tty_compat_ioctl,

.open= tty_open,

.release= tty_release,

.fasync= tty_fasync,

};

 

 

 

2. tty_write找到线路规程的opstty_ldisc_ops,并且调用线路规程ops(.write ) do_tty_write(ld- >ops- >write,tty,file,buf,count)

 static ssize_ttty_write(structfile *file,const char __user*buf,size_tcount,loff_t *ppos)

 {

 structinode *inode= file- >f_path.dentry- >d_inode;

 structtty_struct *tty= file_tty(file);

 structtty_ldisc *ld;

 ssize_tret;

 

 if(tty_paranoia_check(tty,inode,"tty_write"))

 return- EIO;

 if(!tty | | !tty- >ops- >write | |

 (test_bit(TTY_IO_ERROR,&tty- >flags)))

 return- EIO;

 / * Short term debug to catch buggy drivers */

 if(tty- >ops- >write_room ==NULL)

 printk(KERN_ERR"tty driver %s lacks a write_room method.\n",

 tty- >driver- >name);

 ld= tty_ldisc_ref_wait(tty);

 if(!ld- >ops- >write)

 ret= - EIO;

 else

 ret= do_tty_write(ld- >ops- >write, tty,file,buf,count);

 tty_ldisc_deref(ld);

 returnret;

 }

 

 

 

 

 

 

 

 

 

 

 

 

那么,又是谁充当线路规程的ops呢? 就是tty_ldisc_N_TTY (PATH:drivers/tty/n_tty.c),那么tty_write 将调用到n_tty_write 

 

 structtty_ldisc_ops tty_ldisc_N_TTY= {

 .magic =TTY_LDISC_MAGIC,

 .name ="n_tty",

 .open =n_tty_open,

 .close =n_tty_close,

 .flush_buffer =n_tty_flush_buffer,

 .chars_in_buffer =n_tty_chars_in_buffer,

 .read =n_tty_read,

 .write =n_tty_write,

 .ioctl =n_tty_ioctl,

 .set_termios =n_tty_set_termios,

 .poll =n_tty_poll,

 .receive_buf =n_tty_receive_buf,

 .write_wakeup =n_tty_write_wakeup

 };

 

 n_tty_write 调用到tty_operatios里的write函数:

 static ssize_tn_tty_write(structtty_struct *tty,struct file*file,const unsigned char *buf,size_t nr)

 {

  const unsigned char *b= buf;

  DECLARE_WAITQUEUE(wait,current);

  intc;

  ssize_tretval =0;

 

 / * Job control check - - must be done at start (POSIX.1 7.1.1.4). */

  if(L_TOSTOP(tty)&& file- >f_op- >write ! =redirected_tty_write)

  {

  retval= tty_check_change(tty);

  if(retval)

  returnretval;

  }

 

 / * Write out any echoed characters that are still pending */

 process_echoes(tty);

 

 add_wait_queue(&tty- >write_wait, &wait);

  while(1) {

  set_current_state(TASK_INTERRUPTIBLE);

  if(signal_pending(current)){

  retval= - ERESTARTSYS;

 break;

  }

  if(tty_hung_up_p(file)| | (tty- >link && !tty- >link- >count)){

  retval= - EIO;

 break;

 }

  if(O_OPOST(tty)&& ! (test_bit(TTY_HW_COOK_OUT,&tty- >

  flags))){

 while(nr> 0) {

 ssize_tnum =process_output_block(tty,b,nr);

 if(num< 0) {

 if(num== - EAGAIN)

  break;

  retval= num;

  goto¯break_out;

 }

 b+= num;

  nr- = num;

  if(nr== 0)

  break;

 c= *b;

 if(process_output(c,tty)< 0)

  break;

 b++;nr- -;

 }

  if(tty- >ops- >flush_chars)

  tty- >ops- >flush_chars(tty);

  }else {

  while(nr> 0) {

  c= tty- >ops- >write(tty,b,nr);

 if(c< 0) {

 retval= c;

 goto¯break_out;

  }

  if(!c)

  break;

 b+= c;

 nr- = c;

 }

 }

 if(!nr)

 break;

 if(file- >f_flags &O_NONBLOCK) {

 retval= - EAGAIN;

 break;

 }

  schedule();

  }? end while 1 ?

  break_out:

  __set_current_state(TASK_RUNNING);

  remove_wait_queue(&tty- >write_wait, &wait);

  if(b- buf! = nr&& tty- >fasync)

  set_bit(TTY_DO_WRITE_WAKEUP,&tty- >flags);

  return(b- buf)? b- buf: retval;

 }

 

那么,又是谁充当tty_operationsops呢?uart_ops,此操作集在串口驱动注册时指定,注册进tty框架中。,uart_ops找到uart_write函数。

 

(PATH:/driver/tty/serial/serial_core.c)

 static conststruct tty_operationsuart_ops ={

  .open =uart_open,

 .close =uart_close,

  .write =uart_write,

 .put_char =uart_put_char,

  .flush_chars =uart_flush_chars,

 .write_room =uart_write_room,

 .chars_in_buffer=uart_chars_in_buffer,

 .flush_buffer =uart_flush_buffer,

 .ioctl =uart_ioctl,

 .throttle =uart_throttle,

 .unthrottle =uart_unthrottle,

 .send_xchar =uart_send_xchar,

 .set_termios =uart_set_termios,

 .set_ldisc =uart_set_ldisc,

 .stop =uart_stop,

 .start =uart_start,

  .hangup =uart_hangup,

 .break_ctl =uart_break_ctl,

 .wait_until_sent=uart_wait_until_sent,

 #ifdefCONFIG_PROC_FS

 .proc_fops = &uart_proc_fops,

 #endif

 .tiocmget =uart_tiocmget,

 .tiocmset =uart_tiocmset,

 .get_icount =uart_get_icount,

 #ifdefCONFIG_CONSOLE_POLL

 .poll_init =uart_poll_init,

 .poll_get_char =uart_poll_get_char,

 .poll_put_char =uart_poll_put_char,

 #endif

 };

 

 

下面分析下uart_write函数做了什么。首先从tty- >driver_data中取得uart_state *state,然后从*state中取得uart_port *port,然后从*port中拿到了ops,实现了对硬件层的驱动。

static int uart_write(structtty_struct *tty,

 const unsigned char *buf,int count)

 {

 structuart_state *state= tty- >driver_data;

 structuart_port *port;

 structcirc_buf *circ;

 unsigned longflags;

 intc,ret =0;

 

 / *

 * This means you called this function _after_ the port was

 * closed. No cookie for you.

 */

  if(!state) {

 WARN_ON(1);

  return- EL3HLT;

 }

 

  port= state- >uart_port;

 circ= &state- >xmit;

 

  if(!circ- >buf)

  return0;

 

  spin_lock_irqsave(&port- >lock, flags);

  while(1) {

 c= CIRC_SPACE_TO_END(circ- >head,circ- >tail,

  UART_XMIT_SIZE);

  if(count< c)

  c= count;

 if(c<= 0)

  break;

 memcpy(circ- >buf + circ- >head,buf,c);

  circ- >head =(circ- >head +c)& (UART_XMIT_SIZE- 1);

  buf+= c;

  count- = c;

 ret+= c;

 }

  spin_unlock_irqrestore(&port- >lock, flags);

 

 uart_start(tty);/*->由此处拿到port中的ops,分析见下*/

  returnret;

 }? end uart_write ?

 

 

 static voiduart_start(structtty_struct *tty)

 {

  structuart_state *state= tty- >driver_data;

  structuart_port *port= state- >uart_port;

  unsigned longflags;

 

  spin_lock_irqsave(&port- >lock, flags);

  __uart_start(tty);

  spin_unlock_irqrestore(&port- >lock, flags);

 }

static void __uart_start(structtty_struct *tty)

 {

  structuart_state *state= tty- >driver_data;

  structuart_port *port= state- >uart_port;

 

 if(!uart_circ_empty(&state- >xmit)&& state- >xmit.buf &&

 !tty- >stopped && !tty- >hw_stopped)

 port- >ops- >start_tx(port);  /*到此便完成了一次串口写入*/

 }

 

那么,此处的ops类型:

struct uart_ops{

  unsigned int(*tx_empty)(structuart_port *);

  void(*set_mctrl)(structuart_port *,unsigned int mctrl);

  unsigned int(*get_mctrl)(structuart_port *);

  void(*stop_tx)(structuart_port *);

  void(*start_tx)(structuart_port *);

  void(*throttle)(structuart_port *);

 void(*unthrottle)(structuart_port *);

 void(*send_xchar)(structuart_port *,char ch);

  void(*stop_rx)(structuart_port *);

  void(*enable_ms)(structuart_port *);

  void(*break_ctl)(structuart_port *,int ctl);

  int(*startup)(structuart_port *);

  void(*shutdown)(structuart_port *);

 void(*flush_buffer)(structuart_port *);

 void(*set_termios)(structuart_port *,struct ktermios *new,

  structktermios *old);

 void(*set_ldisc)(structuart_port *,int new);

 void(*pm)(structuart_port *,unsigned int state,

 unsigned intoldstate);

 int(*set_wake)(structuart_port *,unsigned int state);

 

 / *

 * Return a string describing the type of the port

 */

  const char *(*type)(structuart_port *);

 

 / *

 * Release IO and memory resources used by the port.

 * This includes iounmap if necessary.

 */

  void(*release_port)(structuart_port *);

 

 / *

  * Request IO and memory resources used by the port.

  * This includes iomapping the port if necessary.

*/

int (*request_port)(structuart_port *);

void (*config_port)(structuart_port *,int);

int (*verify_port)(structuart_port *,struct serial_struct *);

int (*ioctl)(structuart_port *,unsigned int,unsigned long);

#ifdef CONFIG_CONSOLE_POLL

int (*poll_init)(structuart_port *);

void (*poll_put_char)(structuart_port *,unsigned char);

int (*poll_get_char)(structuart_port *);

#endif

} ? end uart_ops ?;

 


0 0