Linux驱动分析之Uart驱动架构

简介: UART设备驱动可以使用tty驱动的框架来实现,但是因为串口之间有共性,所以Linux在tty接口上封装了一层(serial core)。后面我们再拿一篇文章来解释tty驱动,tty其实就是各种终端设备,串口其实也是终端设备。

 Uart体系结构

 

   UART设备驱动可以使用tty驱动的框架来实现,但是因为串口之间有共性,所以Linux在tty接口上封装了一层(serial core)。后面我们再拿一篇文章来解释tty驱动,tty其实就是各种终端设备,串口其实也是终端设备。

   驱动工程师没必要关心上层的流程,只需注册一个uart_driver,并按硬件规范将对应接口函数完成就可以了。

image.gif编辑

上图我们只需要实现xxx_uart.c ,  而我们实现所需要的结构体和函数接口就是由serial_core.c提供。接下来我们来看一下对应的结构体和接口函数。

重要结构体

内核版本:4.20.12

    • uart_driver
    structuart_driver {
    structmodule*owner;
    constchar*driver_name;
    constchar*dev_name; //设备名,即dev下的节点名intmajor;
    intminor;
    intnr;
    structconsole*cons;//console配置,串口作为console时才需要//私有的,底层驱动把它初始化为NULL即可structuart_state*state;
    structtty_driver*tty_driver;
    };

    image.gif

    串口设备也是字符设备,所以看到很多字符设备相关的,console就是控制台,我们平常所使用的debug口就是console。

      • uart_port
      //描述一个UART端口structuart_port {
      spinlock_tlock;      /* port lock */unsignedlongiobase;      /* in/out[bwl] */unsignedchar__iomem*membase;    /* read/write[bwl] */unsignedint    (*serial_in)(structuart_port*, int);
      void      (*serial_out)(structuart_port*, int, int);
      void      (*set_termios)(structuart_port*,
      structktermios*new,
      structktermios*old);
      void      (*set_ldisc)(structuart_port*,
      structktermios*);
      unsignedint    (*get_mctrl)(structuart_port*);
      void      (*set_mctrl)(structuart_port*, unsignedint);
      unsignedint    (*get_divisor)(structuart_port*,
      unsignedintbaud,
      unsignedint*frac);
      void      (*set_divisor)(structuart_port*,
      unsignedintbaud,
      unsignedintquot,
      unsignedintquot_frac);
      int      (*startup)(structuart_port*port);
      void      (*shutdown)(structuart_port*port);
      void      (*throttle)(structuart_port*port);
      void      (*unthrottle)(structuart_port*port);
      //中断处理int      (*handle_irq)(structuart_port*);
      void      (*pm)(structuart_port*, unsignedintstate,
      unsignedintold); //电源管理void      (*handle_break)(structuart_port*);
      //485配置int      (*rs485_config)(structuart_port*,
      structserial_rs485*rs485);
      int      (*iso7816_config)(structuart_port*,
      structserial_iso7816*iso7816);
      unsignedintirq;      /* 中断号 */unsignedlongirqflags;    /* 中断标志  */unsignedintuartclk;    /* 串口时钟 */unsignedintfifosize;    /* tx fifo size */unsignedcharx_char;      /* xon/xoff char */unsignedcharregshift;    /* reg offset shift */unsignedchariotype;      /* io access style */unsignedcharquirks;      /* internal quirks *///省略宏定义....unsignedintread_status_mask;  /* driver specific */unsignedintignore_status_mask;  /* driver specific */structuart_state*state;      /* pointer to parent state */structuart_icounticount;      /* statistics */structconsole*cons;      /* struct console, if any */#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)unsignedlongsysrq;      /* sysrq timeout */#endif/* flags must be updated while holding port mutex */upf_tflags;
      //省略宏定义....upstat_tstatus;
      //省略宏定义....inthw_stopped;    /* sw-assisted CTS flow state */unsignedintmctrl;      /* current modem ctrl settings */unsignedinttimeout;    /* character-based timeout */unsignedinttype;      /* port type */conststructuart_ops*ops;     //串口操作函数unsignedintcustom_divisor;
      unsignedintline;      /* port index */unsignedintminor;
      resource_size_tmapbase;    /* for ioremap */resource_size_tmapsize;
      structdevice*dev;      /* parent device */unsignedcharhub6;      /* this should be in the 8250 driver */unsignedcharsuspended;
      unsignedcharunused[2];
      constchar*name;      /* port name */structattribute_group*attr_group;    /* port specific attributes */conststructattribute_group**tty_groups;  /* all attributes (serial core use only) */structserial_rs485rs485;
      structserial_iso7816iso7816;
      void*private_data;    /* generic platform data pointer */};

      image.gif

      uart_port用于描述一个UART端口的I/O端口或I/O内存地址、FIFO大小、端口类型等信息。这个结构体参数很多,还有很多对串口进行配置的函数。

        • uart_ops
        //物理硬件的所有操作structuart_ops {
        //一些操作函数unsignedint  (*tx_empty)(structuart_port*);//判断发送FIFO是否为空void    (*set_mctrl)(structuart_port*, unsignedintmctrl); //设置控制信息unsignedint  (*get_mctrl)(structuart_port*); //获取当前控制信息void    (*stop_tx)(structuart_port*); //停止txvoid    (*start_tx)(structuart_port*);//启动txvoid    (*throttle)(structuart_port*);//通知串口驱动,线路规程输入缓冲区接近满了void    (*unthrottle)(structuart_port*);//通知串口驱动可以将字符发送到线路规程输入缓冲区void    (*send_xchar)(structuart_port*, charch); //传输高优先级字符,即使端口已停止。void    (*stop_rx)(structuart_port*); //停止Rxvoid    (*enable_ms)(structuart_port*); //使能modem状态中断void    (*break_ctl)(structuart_port*, intctl); //控制中断信号的传输int    (*startup)(structuart_port*); //启动串口void    (*shutdown)(structuart_port*); //关闭串口void    (*flush_buffer)(structuart_port*); //刷新写buffer,复位DMAvoid    (*set_termios)(structuart_port*, structktermios*new,
        structktermios*old); //改变串口参数,包括字长,奇偶校验,停止位。void    (*set_ldisc)(structuart_port*, structktermios*); //通知线路规程改变void    (*pm)(structuart_port*, unsignedintstate,
        unsignedintoldstate); //电源管理//返回一个描述串口类型的字符串constchar*(*type)(structuart_port*);
        //释放IO和内存资源void    (*release_port)(structuart_port*);
        //申请IO和内存资源int    (*request_port)(structuart_port*);
        //配置串口void    (*config_port)(structuart_port*, int);
        int    (*verify_port)(structuart_port*, structserial_struct*);
        int    (*ioctl)(structuart_port*, unsignedint, unsignedlong);
        #ifdef CONFIG_CONSOLE_POLLint    (*poll_init)(structuart_port*);
        void    (*poll_put_char)(structuart_port*, unsignedchar);
        int    (*poll_get_char)(structuart_port*);
        #endif};

        image.gif

        • uart_driver是对tty_driver的封装,uart_driver和platform_driver还是有区别的,因为它并没有probe回调函数。它主要是一些字符设备的信息
        • uart_port用来描述具体的串口,主要是一些串口参数
        • uart_ops就是一些串口的操作函数,和字符设备中的file_operations差不多。


        API函数

        //注册/注销uart_driver到串口核心层intuart_register_driver(structuart_driver*drv)
        voiduart_unregister_driver(structuart_driver*drv)
        //关联具体串口和驱动intuart_add_one_port(structuart_driver*drv, structuart_port*uport)
        //移除串口和驱动的管理intuart_remove_one_port(structuart_driver*drv, structuart_port*uport)

        image.gif

        我们使用到的接口函数很少,所以其实蛮简单的,Linux封装完之后就是填充结构体,然后调用接口注册一下。


        总结

        首先我们要清楚,在底层,Uart驱动是为每个port都分配了缓存空间的。所以应用层读取的都是缓存空间中的。然后uart_driver不能和platform_driver混淆。后面我们分析实例时会发现Uart的驱动是由platform_driver来回调probe的。之前说过,控制器都是使用platform_driver, 串口对于芯片而言,也是一个控制器。


        相关文章
        |
        2月前
        |
        安全 数据处理 数据安全/隐私保护
        C/S架构与B/S架构的适用场景分析
        C/S架构(客户端/服务器架构)与B/S架构(浏览器/服务器架构)在适用场景上各有特点,主要取决于应用的具体需求、用户群体、系统维护成本、跨平台需求等因素。
        149 6
        |
        17天前
        |
        存储 SQL 分布式计算
        湖仓一体架构深度解析:构建企业级数据管理与分析的新基石
        【10月更文挑战第7天】湖仓一体架构深度解析:构建企业级数据管理与分析的新基石
        22 1
        |
        2月前
        |
        存储 监控 安全
        SaaS业务架构:业务能力分析
        【9月更文挑战第20天】在数字化时代,软件即服务(SaaS)模式逐渐成为企业软件解决方案的首选。SaaS 业务架构设计对于提供高效、可靠的服务至关重要。其核心业务能力包括:用户管理(注册登录、角色权限)、数据管理(存储备份、安全共享)、业务流程管理(设计定制、工作流自动化)、应用集成(第三方应用、移动应用)及客户服务(支持培训、反馈改进)。通过优化这些能力,可为企业提供更高效、可靠的 SaaS 服务。
        48 11
        |
        2月前
        |
        编解码 Linux 开发工具
        Linux平台x86_64|aarch64架构RTMP推送|轻量级RTSP服务模块集成说明
        支持x64_64架构、aarch64架构(需要glibc-2.21及以上版本的Linux系统, 需要libX11.so.6, 需要GLib–2.0, 需安装 libstdc++.so.6.0.21、GLIBCXX_3.4.21、 CXXABI_1.3.9)。
        |
        2月前
        |
        存储 传感器 Linux
        STM32微控制器为何不适合运行Linux系统的分析
        总的来说,虽然技术上可能存在某些特殊情况下将Linux移植到高端STM32微控制器上的可能性,但从资源、性能、成本和应用场景等多个方面考虑,STM32微控制器不适合运行Linux系统。对于需要运行Linux的应用,更适合选择ARM Cortex-A系列处理器的开发平台。
        191 0
        |
        2月前
        |
        缓存 负载均衡 数据管理
        深入探索微服务架构的核心要素与实践策略在当今软件开发领域,微服务架构以其独特的优势和灵活性,已成为众多企业和开发者的首选。本文将深入探讨微服务架构的核心要素,包括服务拆分、通信机制、数据管理等,并结合实际案例分析其在不同场景下的应用策略,旨在为读者提供一套全面、深入的微服务架构实践指南。**
        **微服务架构作为软件开发领域的热门话题,正引领着一场技术革新。本文从微服务架构的核心要素出发,详细阐述了服务拆分的原则与方法、通信机制的选择与优化、数据管理的策略与挑战等内容。同时,结合具体案例,分析了微服务架构在不同场景下的应用策略,为读者提供了实用的指导和建议。
        |
        2月前
        |
        Linux API
        Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
        Linux里的高精度时间计时器(HPET)驱动 【ChatGPT】
        |
        3月前
        |
        前端开发 大数据 数据库
        🔥大数据洪流下的决战:JSF 表格组件如何做到毫秒级响应?揭秘背后的性能魔法!💪
        【8月更文挑战第31天】在 Web 应用中,表格组件常用于展示和操作数据,但在大数据量下性能会成瓶颈。本文介绍在 JavaServer Faces(JSF)中优化表格组件的方法,包括数据处理、分页及懒加载等技术。通过后端分页或懒加载按需加载数据,减少不必要的数据加载和优化数据库查询,并利用缓存机制减少数据库访问次数,从而提高表格组件的响应速度和整体性能。掌握这些最佳实践对开发高性能 JSF 应用至关重要。
        63 0
        |
        3月前
        |
        存储 设计模式 运维
        Angular遇上Azure Functions:探索无服务器架构下的开发实践——从在线投票系统案例深入分析前端与后端的协同工作
        【8月更文挑战第31天】在现代软件开发中,无服务器架构因可扩展性和成本效益而备受青睐。本文通过构建一个在线投票应用,介绍如何结合Angular前端框架与Azure Functions后端服务,快速搭建高效、可扩展的应用系统。Angular提供响应式编程和组件化能力,适合构建动态用户界面;Azure Functions则简化了后端逻辑处理与数据存储。通过具体示例代码,详细展示了从设置Azure Functions到整合Angular前端的全过程,帮助开发者轻松上手无服务器应用开发。
        25 0
        |
        8天前
        |
        运维 安全 Linux
        Linux中传输文件文件夹的10个scp命令
        【10月更文挑战第18天】本文详细介绍了10种利用scp命令在Linux系统中进行文件传输的方法,涵盖基础文件传输、使用密钥认证、复制整个目录、从远程主机复制文件、同时传输多个文件和目录、保持文件权限、跨多台远程主机传输、指定端口及显示传输进度等场景,旨在帮助用户在不同情况下高效安全地完成文件传输任务。
        84 5