• linux驱动_uart


    linux uart驱动基础知识下面链接这篇文章写得很完备,我没必要再介绍了,就写目前项目的代码,方便以后重温。
    Linux的tty架构及UART驱动详解

    本项目驱动文件包括:

    /kernel/drivers/sstar/serial/ms_uart.c  # 主要实现文件
    /kernel/drivers/sstar/serial/infinity2m/ms_uart.h # 头文件,定义串口号
    /kernel/drivers/sstar/serial/infinity2m/uart_pads.c # 硬件相关
    
    • 1
    • 2
    • 3

    uart dts(设备树)

    本项目 uart dts文件路径:
    /kernel/arch/arm/boot/dts/infinity2m-doublenet.dtsi
    和uart 相关的内容如下,注意注释的内容:

        aliases {
            console = &uart0;
            serial0 = &uart0; // 别名,驱动会用到serial, 用户层文件系统对应:/dev/ttySx
            serial1 = &uart1; // 对应 of_alias_get_id(pdev->dev.of_node, "serial"), /dev/ttyS1
            serial2 = &fuart;
            serial3 = &uart2;
        };
    
        ...
    
        soc { // 注意uart是在soc下的,是平台设备,所以要注册平台驱动
            ...
            uart0: uart0@1F221000 { // 节点头部格式:[@]
                compatible = "sstar,uart"; // 与驱动程序的 ".of_match_table" 对应
                reg = <0x1F221000 0x100>;  // platform_get_resource(pdev, IORESOURCE_MEM, 0)
                interrupts = ; // platform_get_resource(pdev, IORESOURCE_IRQ, 0)
                clocks = <&CLK_uart0>; // of_clk_get(pdev->dev.of_node, 0)
                status = "ok"; 
            };
            uart1: uart1@1F221200 {
                compatible = "sstar,uart";
                reg = <0x1F221200 0x100>;
                interrupts = ;
                clocks = <&CLK_uart1>;
                status = "ok";
            };
            fuart: uart2@1F220400 {
                compatible = "sstar,uart";
                reg = <0x1F220400 0x100>, <0x1F220600 0x100>;
                interrupts = , ;
                clocks = <&CLK_fuart>;
                dma = <0>;
                sctp_enable = <0>;//rts cts enable is 1
                status = "ok";
            };
            uart2: uart2@1F221400 {
                compatible = "sstar,uart";
                reg = <0x1F221400 0x100>;
                interrupts = ;
                clocks = <&CLK_uart2>;
                status = "disabled"; // 没有使能,所以没有这个设备
            };
            ...
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    数据结构

    参考文章里面提到几个重要数据结构:struct uart_driver, struct console, struct uart_state, struct uart_port, struct uart_ops.在项目代码里面uart_port被封装进struct ms_uart_port结构体了,如下:

    struct ms_uart_port {
        struct uart_port    port;   // 这个是 uart_add_one_port 的参数
        struct ms_urdma    *urdma;
        struct device      *dev;
        struct clk         *clk;
        int use_dma;
    #if UART_TX_TASK
        struct tasklet_struct xmit_tasklet;
    #endif
        int rx_guard;
        u8 backupIER;
        u8 backupLCR;
        u8 backupMCR;
        u16 backupDivisor;
        u8 padmux;
        u8 pad_mode;
        u8 rs485_gpio_flag; // 这个标志位对RS485应用比较重要
        struct timer_list	timer;		/* "no irq" timer */
        u16 bugs;		/* port bugs */
        CamOsThread urdma_task;
    };
    
    static struct ms_uart_port  console_port;
    static struct uart_driver ms_uart_driver; // 全局uart驱动
    
    /* Serial Console Structure Definition */
    static struct console ms_uart_console =   // 全局console
    {
        .name   = MS_CONSOLE_DEV,
        .write  = ms_uart_console_write,
        .setup  = ms_uart_console_setup,
        .flags  = CON_PRINTBUFFER,
    	.device = uart_console_device,
    	.data   = &ms_uart_driver,  // uart 驱动
        .index  = -1,
    #if CONSOLE_DMA
        .match = ms_uart_console_match,
    #endif
    };
    
    static struct uart_driver ms_uart_driver = {
        .owner        = THIS_MODULE,
        .driver_name    = "ms_uart",
        .dev_name    = "ttyS",
        .nr        = 8,
        .cons        = &ms_uart_console, // console
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    以上面代码可以看到,uart驱动包括了console,console里面的data也指向了uart 驱动,uart_port 与driver的关系要看uart 驱动的注册过程。

    驱动注册

    static const struct of_device_id ms_uart_of_match_table[] = {
        { .compatible = "sstar,uart" },
        {}
    };
    MODULE_DEVICE_TABLE(of, ms_uart_of_match_table);
    
    static struct platform_driver ms_uart_platform_driver = {
        .remove = ms_uart_remove,
        .probe = ms_uart_probe,
    #ifdef CONFIG_PM
        .suspend = ms_uart_suspend,
        .resume = ms_uart_resume,
    #endif
        .driver = {
            .name = "ms_uart",
            .owner = THIS_MODULE,
            .of_match_table = of_match_ptr(ms_uart_of_match_table),
        },
    };
    
    static s32 __init ms_uart_module_init(void)
    {
        int ret;
    
        ret = uart_register_driver(&ms_uart_driver);
        if (ret != 0)
            return ret;
        ret = platform_driver_register(&ms_uart_platform_driver);
        if (ret != 0)
        {
            // UART_ERR("[ms_uart]platform_driver_register failed!!\n");
            uart_unregister_driver(&ms_uart_driver);
        }
        return ret;
    }
    
    
    static void __exit ms_uart_module_exit(void)
    {
        platform_driver_unregister(&ms_uart_platform_driver);
        uart_unregister_driver(&ms_uart_driver);
    }
    
    module_init(ms_uart_module_init);
    module_exit(ms_uart_module_exit);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    从上面代码来看,module_init 时注册了 ms_uart_driverms_uart_platform_driver 驱动,ms_uart_platform_driver 指定了设备匹配的table(如下),当内核启动到解析设备树文件时,会调用驱动的probe函数(ms_uart_probe),在probe函数里面,设备和驱动会绑定和初始化。

    static const struct of_device_id ms_uart_of_match_table[] = {
        { .compatible = "sstar,uart" },
        {}
    };
    
    • 1
    • 2
    • 3
    • 4

    probe 过程

    下面是经过简化处理的探测函数,读一下注释基本了解执行过程。

    /* UART Operations */
    static struct uart_ops ms_uart_ops =
    {
        .tx_empty     = ms_uart_tx_empty,      // 串口的Tx FIFO缓存是否为空
        .set_mctrl    = ms_uart_set_mctrl,     // 设置串口modem控制
        .get_mctrl    = ms_uart_get_mctrl,     // 获取串口modem控制
        .stop_tx      = ms_uart_stop_tx,       // 禁止串口发送数据
        .start_tx     = ms_uart_start_tx,      // 使能串口发送数据
        .stop_rx      = ms_uart_stop_rx,       // 禁止串口接收数据
        .enable_ms    = ms_uart_enable_ms,     // 使能modem的状态信号
        .break_ctl    = ms_uart_break_ctl,     // 设置break信号
        .startup      = ms_uart_startup,       // 启动串口,应用程序打开串口设备文件时,该函数会被调用
        .shutdown     = ms_uart_shutdown,      // 关闭串口,应用程序关闭串口设备文件时,该函数会被调用
        .set_termios  = ms_uart_set_termios,   // 设置串口参数
        .type         = ms_uart_type,          // 返回一描述串口类型的字符串
        .release_port = ms_uart_release_port,  // 释放串口已申请的IO端口/IO内存资源,必要时还需iounmap
        .request_port = ms_uart_request_port,  // 申请必要的IO端口/IO内存资源,必要时还可以重新映射串口端口
        .config_port  = ms_uart_config_port,   // 执行串口所需的自动配置
        .verify_port  = ms_uart_verify_port,   // 核实新串口的信息
        //.ioctl // IO控制
    };
    
    static s32 ms_uart_probe(struct platform_device *pdev)
    {
        struct ms_uart_port *mp;
        struct resource *res;
    
        // new 了一个 ms_uart_port
        mp = devm_kzalloc(&pdev->dev, sizeof(*mp), GFP_KERNEL);
    
        // 初始化 uart_port 的spin_lock
        spin_lock_init(&mp->port.lock);
    
        // 读取 dts 里面的 "serialx = &uartx", mp->port.line = x
        mp->port.line = of_alias_get_id(pdev->dev.of_node, "serial");
        pdev->id=mp->port.line;
    
        // 读取dts的 "clocks = <&CLK_uart0>;"
        mp->clk = of_clk_get(pdev->dev.of_node, 0);
    
        //enable clk in probe, because if UART no clk, it can not be accessed.
        clk_prepare_enable(mp->clk);
        mp->port.uartclk = clk_get_rate(mp->clk);
    
        // 读取dts的 "reg = <0x1F221000 0x100>;"
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        mp->port.membase = (void *)res->start;
    
        // 读取dts的 "interrupts = ;"
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        mp->port.irq = res->start;
    
        // 读取dts的"dma = <0>;"
        of_property_read_u32(pdev->dev.of_node, "dma", &mp->use_dma);
        if (mp->use_dma)
        { // dts里面值为0,所有dma相关的代码都忽略了
        }
    
        // 继续初始化 struct uart_port 结构体
        mp->port.type = PORT_8250;
        mp->port.dev = &pdev->dev;
        mp->port.ops=&ms_uart_ops;  // 这里指定了上面定义的所有操作函数
        mp->port.regshift = 0;
        mp->port.fifosize = 16;     // 16 字节,可能跟硬件相关,没datasheet不清楚
        mp->port.timeout  =HZ;
        mp->port.iotype=UPIO_MEM;
        mp->port.rs485_config = ms_uart_rs485_config; // rs485 配置函数,用户层可能会调用到
    
        // 绑定硬件,因为dts没指定"pad",所以of_property_read_u32返回非0值
        if (of_property_read_u32(pdev->dev.of_node, "pad", &tx_pad))  //read property failed
        {
            //set default pad
            if(mp->port.line==0)
                mp->padmux=MUX_PM_UART;
            else if(mp->port.line==1)
                mp->padmux=MUX_UART1;
            else if(mp->port.line==2)
                mp->padmux=MUX_FUART;
            else if(mp->port.line==3)
                mp->padmux=MUX_UART2;
            else
                ret = -EINVAL;
        }
        else  //read property successfully
        {
            if (ms_uart_get_padmux(tx_pad, &(mp->padmux), &(mp->pad_mode)) != 0)
                ret = -EINVAL;
        }
    
        // 把new出来的 struct ms_uart_port 保存到串口设备去
        platform_set_drvdata(pdev, mp);
    
        // 关键在这里,把struct uart_port 加到 uart_driver去了
        ret = uart_add_one_port(&ms_uart_driver, &mp->port);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    数据发送接收流程

    tty设备发送、接收数据流程图如下(别人的图):
    tty设备发送、接收数据流程图

    发送
    从上面 ms_uart_ops 可知,用户层发送数据会调用 “.start_tx = ms_uart_start_tx”。基本流程是先把RS485使能(如果是RS485口),然后使能uart发送缓存为空中断,在中断处理函数 ms_uart_interrupt 里面调用 ms_putchar 函数,然后从“ **struct circ_buf xmit = struct uart_port p->state->xmit ”环回缓存里面取数据并塞到uart 数据发送缓存寄存器,发送完就调用 ms_uart_stop_tx 停止发送。发送数据流程如下图(别人的图):
    发送数据流程

    接收
    在中断处理函数ms_uart_interrupt 接收分支里面调用 ms_getchar 函数,从寄存器读取数据,调用 uart_insert_char 函数往上层送。接收数据流程图如下(别人的图):
    接收数据流程图
    其它的开发内容基本是 ms_uart_ops 回调函数的实现,不一一解读了。

    end

  • 相关阅读:
    企业电子招投标采购系统源码之电子招投标的组成
    P1182 数列分段 Section II——二分答案
    Codeforces Round 908 (Div. 2)
    PDF格式分析(七十四)——自由文本注释(Free Text)
    QTimer::singleShot问题及用法
    Go-知识error
    modbus协议教程
    柏林自由大学研究团队《Ecology Letters 》揭示AMF在植物对全球变化响应的作用
    基本地址变换机构
    刷题日常计~JS①
  • 原文地址:https://blog.csdn.net/gavinpeng/article/details/126965121