linux下TTY驱动(serial)

/dev/console是系统控制台,是与操作系统交互的设备,系统所产生的信息会发送到该设备上。

如果一个终端设备要实现console功能必须向内核注册struct console结构,如果要实现tty功能,要向内核tty子系统注册struct tty_driver结构。

tty0是系统自动打开的,但不用于用户登录。在framebuffer设备没有启用的系统中,可以使用/dev/tty0访问显卡。

串行通信设备驱动是不能被用户直接使用的,必须被抽象为一个tty设备,然后配置使用默认的线路规则(n_tty.c),经tty设备驱动子系统在系统中注册为字符设备。

linux默认线路规则是N_TTY(标准字符终端I/O处理规则).

write函数的阻塞版本在内核里使用等待队列实现的。


  之前因为是刚入门所以看了串口有关的东西,一开始看了stm32f407上的串口编程(Keil MDK),那算是裸的驱动了。 

 linux下的串口如果要正常工作的话,就必须通过TTY这个子系统,TTY子系统算是比较复杂,概念也是比较复杂的,有三类驱动程序:控制台,串口,pty。任何tty驱动程序的主要数据结构是结构tty_driver.概念性的问题还是去看一下《linux设备驱动程序》这本书,它也算是驱动工程师必读的圣经 了。接下来就开始直接看代码了。

 从xxx_uart_probe函数开始(至于为什么希望看一下我之前写的platform_device).

         res = platform_get_resource(dev, IORESOURCE_MEM, 0);
         if(res == NULL)
         {
                 ret = -ENOMEM;
                 goto free;
         }
 
         base = ioremap(res->start, res->end-res->start);
         if (!base)
         {
                 ret = -ENOMEM;
                 goto free;
         }
 
         sprintf(uart_name, "%s", dev->name + 6);
         uap->clk = clk_get(&dev->dev, uart_name);
         if (IS_ERR(uap->clk)) {
                 ret = PTR_ERR(uap->clk);
                 goto unmap;
         }
  一开始依旧是获取一些之前platform_device已经注册号的资源(中断,内存空间和时钟等等)。同时注册一些uart_ops回调函数,这些回调函数是驱动层自己实现的,最后上层的相关函数最终都会调用到这些回调函数。

需要注意的是,在tty子系统中,要注册3次operation函数,但是每次的operation都是不同的,有调用关系,我把另外两个先贴出来:

 最后调用serial_core.c中的add_one_port();之后所有的操作都由TTY子系统自动去完成了。

 static struct uart_ops amba_pl011_pops = {
         .tx_empty       = pl01x_tx_empty,
         .set_mctrl      = pl011_set_mctrl,
         .get_mctrl      = pl01x_get_mctrl,
         .stop_tx        = pl011_stop_tx,
         .start_tx       = pl011_start_tx,
         .stop_rx        = pl011_stop_rx,
         .enable_ms      = pl011_enable_ms,
         .break_ctl      = pl011_break_ctl,
         .startup        = pl011_startup,
         .shutdown       = pl011_shutdown,
         .flush_buffer   = pl011_dma_flush_buffer,
         .set_termios    = pl011_set_termios,
         .type           = pl011_type,
         .release_port   = pl010_release_port,
         .request_port   = pl010_request_port,
         .config_port    = pl010_config_port,
         .verify_port    = pl010_verify_port,
 #ifdef CONFIG_CONSOLE_POLL
         .poll_get_char = pl010_get_poll_char,
         .poll_put_char = pl010_put_poll_char,
 #endif
 };

uart_configure_port();

tty_register_device();//注册设备到tty框架中去

device_create(); 

注册tty_device的顺序是xxx_uart_probe()->add_one_port()->tty_register_device().

/tty/serial/serial_core.c

uart_register_driver();//这个函数是uart框架自己提供的函数,我们只要将参数传递进去,该框架就会自动注册该驱动。

在这个函数中主要是分配tty_driver结构体的内存空间,初始化一些默认的值。并且注册一些tty_operation相关的回调函数。

tty_set_operations(normal, &uart_ops);

 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
 };
tty/tty_io.c

最终调用tty_register_driver();//注册驱动到tty框架中去。

cdev_init(&driver->cdev, &tty_fops);

cdev_add();

  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,
  };

注册tty_driver的顺序是uart_register_driver()->tty_register_driver().

tty/n_tty.c

(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);//我们在程序中设置串口数据格式和波特率都是在下面回调函数中的ioctl来设置的。

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

tty_ldisc_begin();//设置默认的线路规程

console_init();


  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值