了解UART串口驱动

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_struct xmit是数据发送缓冲区,用来接收上层用户传递过来的数据。

 

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_t tty_write(struct file *file, const char __user *buf,size_t count, loff_t *ppos)

 {

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

 struct tty_struct *tty = file_tty(file);

 struct tty_ldisc *ld;

 ssize_t ret;

 

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

 return ret;

 }

 

 

 

 

 

 

 

 

 

 

 

 

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

 

 struct tty_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_t n_tty_write(struct tty_struct *tty, struct file *file,const unsigned char *buf, size_t nr)

 {

  const unsigned char *b = buf;

  DECLARE_WAITQUEUE(wait, current);

  int c;

  ssize_t retval = 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)

  return retval;

  }

 

 / * 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_t num = 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 const struct tty_operations uart_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,

 #ifdef CONFIG_PROC_FS

 .proc_fops = &uart_proc_fops,

 #endif

 .tiocmget = uart_tiocmget,

 .tiocmset = uart_tiocmset,

 .get_icount = uart_get_icount,

 #ifdef CONFIG_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(struct tty_struct *tty,

 const unsigned char *buf, int count)

 {

 struct uart_state *state = tty- >driver_data;

 struct uart_port *port;

 struct circ_buf *circ;

 unsigned long flags;

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

  return 0;

 

  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,分析见下*/

  return ret;

 } ? end uart_write ?

 

 

 static void uart_start(struct tty_struct *tty)

 {

  struct uart_state *state = tty- >driver_data;

  struct uart_port *port = state- >uart_port;

  unsigned long flags;

 

  spin_lock_irqsave(&port- >lock, flags);

  __uart_start(tty);

  spin_unlock_irqrestore(&port- >lock, flags);

 }

static void __uart_start(struct tty_struct *tty)

 {

  struct uart_state *state = tty- >driver_data;

  struct uart_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)(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 (*throttle)(struct uart_port *);

 void (*unthrottle)(struct uart_port *);

 void (*send_xchar)(struct uart_port *, char ch);

  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 *, int new);

 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

int (*poll_init)(struct uart_port *);

void (*poll_put_char)(struct uart_port *, unsigned char);

int (*poll_get_char)(struct uart_port *);

#endif

} ? end uart_ops ? ;

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值