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, 串口对于芯片而言,也是一个控制器。


        相关文章
        |
        27天前
        |
        运维 监控 负载均衡
        动态服务管理平台:驱动微服务架构的高效引擎
        动态服务管理平台:驱动微服务架构的高效引擎
        24 0
        |
        3月前
        |
        安全 数据处理 数据安全/隐私保护
        C/S架构与B/S架构的适用场景分析
        C/S架构(客户端/服务器架构)与B/S架构(浏览器/服务器架构)在适用场景上各有特点,主要取决于应用的具体需求、用户群体、系统维护成本、跨平台需求等因素。
        290 6
        |
        1月前
        |
        存储 SQL Apache
        Apache Doris 开源最顶级基于MPP架构的高性能实时分析数据库
        Apache Doris 是一个基于 MPP 架构的高性能实时分析数据库,以其极高的速度和易用性著称。它支持高并发点查询和复杂分析场景,适用于报表分析、即席查询、数据仓库和数据湖查询加速等。最新发布的 2.0.2 版本在性能、稳定性和多租户支持方面有显著提升。社区活跃,已广泛应用于电商、广告、用户行为分析等领域。
        Apache Doris 开源最顶级基于MPP架构的高性能实时分析数据库
        |
        1月前
        |
        Java Linux Android开发
        深入探索Android系统架构:从Linux内核到应用层
        本文将带领读者深入了解Android操作系统的复杂架构,从其基于Linux的内核到丰富多彩的应用层。我们将探讨Android的各个关键组件,包括硬件抽象层(HAL)、运行时环境、以及核心库等,揭示它们如何协同工作以支持广泛的设备和应用。通过本文,您将对Android系统的工作原理有一个全面的认识,理解其如何平衡开放性与安全性,以及如何在多样化的设备上提供一致的用户体验。
        |
        1月前
        |
        缓存 运维 网络协议
        深入Linux内核架构:操作系统的核心奥秘
        深入Linux内核架构:操作系统的核心奥秘
        53 2
        |
        1月前
        |
        运维 NoSQL Java
        后端架构演进:微服务架构的优缺点与实战案例分析
        【10月更文挑战第28天】本文探讨了微服务架构与单体架构的优缺点,并通过实战案例分析了微服务架构在实际应用中的表现。微服务架构具有高内聚、低耦合、独立部署等优势,但也面临分布式系统的复杂性和较高的运维成本。通过某电商平台的实际案例,展示了微服务架构在提升系统性能和团队协作效率方面的显著效果,同时也指出了其带来的挑战。
        82 4
        |
        2月前
        |
        消息中间件 监控 NoSQL
        驱动系统架构
        【10月更文挑战第29天】
        32 2
        |
        2月前
        |
        存储 SQL 分布式计算
        湖仓一体架构深度解析:构建企业级数据管理与分析的新基石
        【10月更文挑战第7天】湖仓一体架构深度解析:构建企业级数据管理与分析的新基石
        144 1
        |
        2月前
        |
        存储 前端开发 API
        DDD领域驱动设计实战-分层架构
        DDD分层架构通过明确各层职责及交互规则,有效降低了层间依赖。其基本原则是每层仅与下方层耦合,分为严格和松散两种形式。架构演进包括传统四层架构与改良版四层架构,后者采用依赖反转设计原则优化基础设施层位置。各层职责分明:用户接口层处理显示与请求;应用层负责服务编排与组合;领域层实现业务逻辑;基础层提供技术基础服务。通过合理设计聚合与依赖关系,DDD支持微服务架构灵活演进,提升系统适应性和可维护性。
        |
        3月前
        |
        存储 监控 安全
        SaaS业务架构:业务能力分析
        【9月更文挑战第20天】在数字化时代,软件即服务(SaaS)模式逐渐成为企业软件解决方案的首选。SaaS 业务架构设计对于提供高效、可靠的服务至关重要。其核心业务能力包括:用户管理(注册登录、角色权限)、数据管理(存储备份、安全共享)、业务流程管理(设计定制、工作流自动化)、应用集成(第三方应用、移动应用)及客户服务(支持培训、反馈改进)。通过优化这些能力,可为企业提供更高效、可靠的 SaaS 服务。
        71 11