为您推荐:
精华内容
最热下载
问答
  • 4星
    39.09MB haojunjun 2021-08-01 19:29:38
  • 5星
    159KB qq_17556735 2021-04-05 22:35:13
  • 5星
    9.68MB weixin_44042579 2020-12-12 11:50:12
  • 5星
    1.37MB cuihao1995 2021-05-22 15:04:41
  • #include #include #include #include #include #include #include #include "reg.h"static void *vbase;static unsigned int vsize;static void reg_unmap(void){if(vbase){iounmap(vbase);}vbase = NULL;...

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include "reg.h"

    static void *vbase;

    static unsigned int vsize;

    static void reg_unmap(void)

    {

    if(vbase){

    iounmap(vbase);

    }

    vbase = NULL;

    }

    static long reg_set_map(struct file *file, unsigned long arg)

    {

    struct reg_base base;

    if(copy_from_user(&base, (void*)arg, sizeof(base))){

    return -EFAULT;

    }

    reg_unmap();

    vbase = ioremap(base.base, base.size);

    if(NULL == vbase){

    return -EINVAL;

    }

    vsize = base.size;

    return 0;

    }

    static long reg_read_value(struct file *file, unsigned long arg)

    {

    struct reg_value value;

    if(copy_from_user(&value, (void*)arg, sizeof(value))){

    return -EFAULT;

    }

    if(NULL == vbase || value.addr > vsize - 4){

    return -EINVAL;

    }

    value.value = readl(vbase + value.addr);

    if(copy_to_user((void*)arg, &value, sizeof(value))){

    return -EFAULT;

    }

    return 0;

    }

    static long reg_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

    {

    long ret = -ENOIOCTLCMD;

    switch(cmd)

    {

    case REG_CMD_BASE:

    ret = reg_set_map(file, arg);

    break;

    case REG_CMD_VALUE:

    ret = reg_read_value(file, arg);

    break;

    default:

    break;

    }

    return ret;

    }

    static struct file_operations fops =

    {

    .unlocked_ioctl = reg_ioctl,

    };

    static struct miscdevice reg_dev =

    {

    .name = "reg",

    .fops = &fops,

    .minor = MISC_DYNAMIC_MINOR,

    };

    static int __init reg_init(void)

    {

    if(misc_register(&reg_dev)){

    printk(KERN_ERR "Failed to register reg\n");

    return -1;

    }

    printk(KERN_INFO "Success to register reg\n");

    return 0;

    }

    static void __exit reg_exit(void)

    {

    misc_deregister(&reg_dev);

    reg_unmap();

    printk(KERN_INFO "Success to remove reg\n");

    }

    module_init(reg_init);

    module_exit(reg_exit);

    MODULE_LICENSE("GPL v2");

    展开全文
    weixin_39716044 2021-05-16 11:16:56
  • 一、核心数据结构串口驱动有3个核心数据结构,它们都定义在1、uart_driveruart_driver包含了串口设备名、串口驱动名、主次设备号、串口控制台(可选)等信息,还封装了tty_driver(底层串口驱动无需关心tty_driver)。...

    一、核心数据结构

    串口驱动有3个核心数据结构,它们都定义在

    1、uart_driver

    uart_driver包含了串口设备名、串口驱动名、主次设备号、串口控制台(可选)等信息,还封装了tty_driver(底层串口驱动无需关心tty_driver)。

    struct uart_driver {

    struct module     *owner;           /* 拥有该uart_driver的模块,一般为THIS_MODULE */

    const char        *driver_name;     /* 串口驱动名,串口设备文件名以驱动名为基础 */

    const char        *dev_name;        /* 串口设备名 */

    int                major;           /* 主设备号 */

    int                minor;           /* 次设备号 */

    int                nr;              /* 该uart_driver支持的串口个数(最大) */

    struct console    *cons;            /* 其对应的console.若该uart_driver支持serial console,否则为NULL *//*

    * these are private; the low level driver should not

    * touch these; they should be initialised to NULL

    */

    struct uart_state *state;

    struct tty_driver *tty_driver;

    };

    2、uart_port

    uart_port用于描述串口端口的I/O端口或I/O内存地址、FIFO大小、端口类型、串口时钟等信息。实际上,一个uart_port实例对应一个串口设备

    struct uart_port {

    spinlock_t             lock;           /* 串口端口锁 */

    unsigned int           iobase;         /* IO端口基地址 */

    unsigned char __iomem *membase;    /* IO内存基地址,经映射(如ioremap)后的IO内存虚拟基地址 */

    unsigned int           irq;           /* 中断号 */

    unsigned int           uartclk;        /* 串口时钟 */

    unsigned int           fifosize;       /* 串口FIFO缓冲大小 */

    unsigned char          x_char;         /* xon/xoff字符 */

    unsigned char          regshift;       /* 寄存器位移 */

    unsigned char          iotype;         /* IO访问方式 */

    unsigned char          unused1;

    #define UPIO_PORT        (0)               /* IO端口 */

    #define UPIO_HUB6        (1)

    #define UPIO_MEM         (2)               /* IO内存 */

    #define UPIO_MEM32       (3)

    #define UPIO_AU          (4)               /* Au1x00 type IO */

    #define UPIO_TSI         (5)               /* Tsi108/109 type IO */

    #define UPIO_DWAPB       (6)               /* DesignWare APB UART */

    #define UPIO_RM9000      (7)               /* RM9000 type IO */

    unsigned int        read_status_mask;  /* 关心的Rx error status */

    unsigned int        ignore_status_mask;/* 忽略的Rx error status */

    struct uart_info      *info;           /* pointer to parent info */

    struct uart_icount     icount;         /* 计数器 */

    struct console        *cons;           /* console结构体 */

    #ifdef CONFIG_SERIAL_CORE_CONSOLE

    unsigned long         sysrq;           /* sysrq timeout */

    #endif

    upf_t                 flags;

    #define UPF_FOURPORT         ((__force upf_t) (1 << 1))

    #define UPF_SAK              ((__force upf_t) (1 << 2))

    #define UPF_SPD_MASK         ((__force upf_t) (0x1030))

    #define UPF_SPD_HI           ((__force upf_t) (0x0010))

    #define UPF_SPD_VHI          ((__force upf_t) (0x0020))

    #define UPF_SPD_CUST         ((__force upf_t) (0x0030))

    #define UPF_SPD_SHI          ((__force upf_t) (0x1000))

    #define UPF_SPD_WARP         ((__force upf_t) (0x1010))

    #define UPF_SKIP_TEST        ((__force upf_t) (1 << 6))

    #define UPF_AUTO_IRQ         ((__force upf_t) (1 << 7))

    #define UPF_HARDPPS_CD       ((__force upf_t) (1 << 11))

    #define UPF_LOW_LATENCY      ((__force upf_t) (1 << 13))

    #define UPF_BUGGY_UART       ((__force upf_t) (1 << 14))

    #define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))

    #define UPF_CONS_FLOW        ((__force upf_t) (1 << 23))

    #define UPF_SHARE_IRQ        ((__force upf_t) (1 << 24))

    #define UPF_BOOT_AUTOCONF    ((__force upf_t) (1 << 28))

    #define UPF_FIXED_PORT       ((__force upf_t) (1 << 29))

    #define UPF_DEAD             ((__force upf_t) (1 << 30))

    #define UPF_IOREMAP          ((__force upf_t) (1 << 31))

    #define UPF_CHANGE_MASK      ((__force upf_t) (0x17fff))

    #define UPF_USR_MASK         ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))

    unsigned int             mctrl;        /* 当前的moden设置 */

    unsigned int             timeout;      /* character-based timeout */

    unsigned int             type;         /* 端口类型 */

    const struct uart_ops   *ops;          /* 串口端口操作函数集 */

    unsigned int             custom_divisor;

    unsigned int             line;         /* 端口索引 */

    resource_size_t          mapbase;      /* IO内存物理基地址,可用于ioremap */

    struct device           *dev;          /* 父设备 */

    unsigned char            hub6;         /* this should be in the 8250 driver */

    unsigned char            suspended;

    unsigned char            unused[2];

    void                    *private_data; /* 端口私有数据,一般为platform数据指针 */

    };

    uart_iconut为串口信息计数器,包含了发送字符计数、接收字符计数等。在串口的发送中断处理函数和接收中断处理函数中,我们需要管理这些计数。

    struct uart_icount {

    __u32    cts;

    __u32    dsr;

    __u32    rng;

    __u32    dcd;

    __u32    rx;/* 发送字符计数 */

    __u32    tx;/* 接受字符计数 */

    __u32    frame;/* 帧错误计数 */

    __u32    overrun;/* Rx FIFO溢出计数 */

    __u32    parity;/* 帧校验错误计数 */

    __u32    brk;/* break计数 */

    __u32    buf_overrun;

    };

    uart_info有两个成员在底层串口驱动会用到:xmit和tty。用户空间程序通过串口发送数据时,上层驱动将用户数据保存在xmit;而串口发送中断处理函数就是通过xmit获取到用户数据并将它们发送出去。串口接收中断处理函数需要通过tty将接收到的数据传递给行规则层。

    /* uart_info实例仅在串口端口打开时有效,它可能在串口关闭时被串口核心层释放。因此,在使用uart_port的uart_info成员时必须保证串口已打开。底层驱动和核心层驱动都可以修改uart_info实例。

    * This is the state information which is only valid when the port

    * is open; it may be freed by the core driver once the device has

    * been closed. Either the low level driver or the core can modify

    * stuff here.

    */

    struct uart_info {

    struct tty_struct     *tty;

    struct circ_buf        xmit;

    uif_t                  flags;/*

    * Definitions for info->flags. These are _private_ to serial_core, and

    * are specific to this structure. They may be queried by low level drivers.

    */

    #define UIF_CHECK_CD        ((__force uif_t) (1 << 25))

    #define UIF_CTS_FLOW        ((__force uif_t) (1 << 26))

    #define UIF_NORMAL_ACTIVE    ((__force uif_t) (1 << 29))

    #define UIF_INITIALIZED        ((__force uif_t) (1 << 31))

    #define UIF_SUSPENDED        ((__force uif_t) (1 << 30))

    int                     blocked_open;

    struct tasklet_struct   tlet;

    wait_queue_head_t       open_wait;

    wait_queue_head_t       delta_msr_wait;

    };

    3、uart_ops

    uart_ops涵盖了串口驱动可对串口设备进行的所有操作。

    /*

    * This structure describes all the operations that can be

    * done on the physical hardware.

    */

    struct uart_ops {

    unsigned int (*tx_empty)(struct uart_port *); /* 串口的Tx FIFO缓存是否为空 */

    void         (*set_mctrl)(struct uart_port *, unsigned int mctrl);/* 设置串口modem控制 */

    unsigned int (*get_mctrl)(struct uart_port *);/* 获取串口modem控制 */

    void         (*stop_tx)(struct uart_port *);/* 禁止串口发送数据 */

    void         (*start_tx)(struct uart_port *);/* 使能串口发送数据 */

    void         (*send_xchar)(struct uart_port *, char ch);/* 发送xChar */

    void         (*stop_rx)(struct uart_port *);/* 禁止串口接收数据 */

    void         (*enable_ms)(struct uart_port *);/* 使能modem的状态信号 */

    void         (*break_ctl)(struct uart_port *, int ctl);/* 设置break信号 */

    int          (*startup)(struct uart_port *);/* 启动串口,应用程序打开串口设备文件时,该函数会被调用 */

    void         (*shutdown)(struct uart_port *);/* 关闭串口,应用程序关闭串口设备文件时,该函数会被调用 */

    void         (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old);/* 设置串口参数 */

    void         (*pm)(struct uart_port *, unsigned int state,

    unsigned int oldstate);/* 串口电源管理 */

    int          (*set_wake)(struct uart_port *, unsigned int state);/*  */

    const char  *(*type)(struct uart_port *);/* 返回一描述串口类型的字符串 */

    void         (*release_port)(struct uart_port *);/* 释放串口已申请的IO端口/IO内存资源,必要时还需iounmap */

    int          (*request_port)(struct uart_port *);/* 申请必要的IO端口/IO内存资源,必要时还可以重新映射串口端口 */

    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);/* IO控制 */

    };

    二、串口驱动API

    1、uart_register_driver

    /* 功能:    uart_register_driver用于将串口驱动uart_driver注册到内核(串口核心层)中,通常在模块初始化函数调用该函数。

    * 参数 drv:要注册的uart_driver

    * 返回值:  成功,返回0;否则返回错误码

    */

    int uart_register_driver(struct uart_driver *drv)

    2、uart_unregister_driver

    /* 功能:    uart_unregister_driver用于注销我们已注册的uart_driver,通常在模块卸载函数调用该函数

    *参数 drv:要注销的uart_driver

    * 返回值:  成功,返回0;否则返回错误码

    */

    void uart_unregister_driver(struct uart_driver *drv)

    3、uart_add_one_port

    /* 功能:    uart_add_one_port用于为串口驱动添加一个串口端口,通常在探测到设备后(驱动的设备probe方法)调用该函数

    * 参数 drv:串口驱动

    *      port:要添加的串口端口

    * 返回值:成功,返回0;否则返回错误码

    */

    int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)

    4、uart_remove_one_port

    /* 功能:     uart_remove_one_port用于删除一个已添加到串口驱动中的串口端口,通常在驱动卸载时调用该函数

    * 参数 drv: 串口驱动

    *      port: 要删除的串口端口

    * 返回值:   成功,返回0;否则返回错误码

    */

    int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)

    5、uart_write_wakeup

    /* 功能:     uart_write_wakeup唤醒上层因向串口端口写数据而阻塞的进程,通常在串口发送中断处理函数中调用该函数

    * 参数 port:需要唤醒写阻塞进程的串口端口

    */

    void uart_write_wakeup(struct uart_port *port)

    6、uart_suspend_port

    /* 功能:     uart_suspend_port用于挂起特定的串口端口

    * 参数drv: 要挂起的串口端口所属的串口驱动

    *      port:要挂起的串口端口

    * 返回值:   成功返回0;否则返回错误码

    */

    int uart_suspend_port(struct uart_driver *drv, struct uart_port *port)

    7、uart_resume_port

    /* 功能:     uart_resume_port用于恢复某一已挂起的串口

    * 参数drv: 要恢复的串口端口所属的串口驱动

    *      port:要恢复的串口端口

    * 返回值:   成功返回0;否则返回错误码

    */

    int uart_resume_port(struct uart_driver *drv, struct uart_port *port)

    8、uart_get_baud_rate

    /* 功能:        uart_get_baud_rate通过解码termios结构体来获取指定串口的波特率

    * 参数 port:  要获取波特率的串口端口

    *     termios:当前期望的termios配置(包含串口波特率)

    *     old:    以前的termios配置,可以为NULL

    *     min:    可接受的最小波特率

    *     max:    可接受的最大波特率

    * 返回值:     串口的波特率

    */

    unsigned int

    uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,

    struct ktermios *old, unsigned int min, unsigned int max)

    9、uart_get_divisor

    /* 功能:     uart_get_divisor用于计算某一波特率的串口时钟分频数(串口波特率除数)

    * 参数 port:要计算时钟分频数的串口端口

    *      baud:期望的波特率

    *返回值:    串口时钟分频数

    */

    unsigned int uart_get_divisor(struct uart_port *port, unsigned int baud)

    10、uart_update_timeout

    /* 功能:      uart_update_timeout用于更新(设置)串口FIFO超时时间

    * 参数 port: 要更新超时时间的串口端口

    *     cflag:termios结构体的cflag值

    *     baud: 串口的波特率*/

    void uart_update_timeout(struct uart_port *port, unsigned intcflag, unsigned int baud)

    11、uart_match_port

    /* 功能:uart_match_port用于判断两串口端口是否为同一端口

    * 参数 port1、port2:要判断的串口端口

    * 返回值:不同返回0;否则返回非0

    */

    int uart_match_port(struct uart_port *port1, struct uart_port *port2)

    12、uart_console_write

    /* 功能:        uart_console_write用于向串口端口写一控制台信息

    * 参数 port:    要写信息的串口端口

    *     s:       要写的信息

    *     count:   信息的大小

    *     putchar: 用于向串口端口写字符的函数,该函数函数有两个参数:串口端口和要写的字符

    */

    void uart_console_write(struct uart_port *port, const char *s,

    unsigned int count,

    void (*putchar)(struct uart_port *, int))

    三、串口驱动例子

    该串口驱动例子是我针对s3c2410处理器的串口2(uart2)独立开发的。因为我通过博创2410s开发板的GRPS扩展板来测试该驱动(已通过测试),所以我叫该串口为gprs_uart。

    该驱动将串口看作平台(platform)设备。platform可以看作一伪总线,用于将集成于片上系统的轻量级设备与Linux设备驱动模型联系到一起,它包含以下两部分(有关platform的声明都在#include ,具体实现在drivers/base/platform.c):

    1、platform设备。我们需要为每个设备定义一个platform_device实例

    struct platform_device {

    const char      *name;/* 设备名 */

    int              id;/* 设备的id号 */

    struct device    dev;/* 其对应的device */

    u32              num_resources;/* 该设备用有的资源数 */

    struct resource *resource;/* 资源数组 */

    };

    为我们的设备创建platform_device实例有两种方法:填充一个platform_device结构体后用platform_device_register(一次注册一个)或platform_add_devices(一次可以注册多个platform设备)将platform_device注册到内核;更简单的是使用platform_device_register_simple来建立并注册我们的platform_device。

    2、platform驱动。platform设备由platform驱动进行管理。当设备加入到系统中时,platform_driver的probe方法会被调用来见对应的设备添加或者注册到内核;当设备从系统中移除时,platform_driver的remove方法会被调用来做一些清理工作,如移除该设备的一些实例、注销一些已注册到系统中去的东西。

    struct platform_driver {

    int  (*probe)(struct platform_device *);

    int  (*remove)(struct platform_device *);

    void (*shutdown)(struct platform_device *);

    int  (*suspend)(struct platform_device *, pm_message_t state);

    int  (*suspend_late)(struct platform_device *, pm_message_t state);

    int  (*resume_early)(struct platform_device *);

    int  (*resume)(struct platform_device *);

    struct device_driver driver;

    };

    更详细platform资料可参考网上相关文章。

    例子驱动中申请和释放IO内存区的整个过程如下:

    insmod gprs_uart.ko→gprs_init_module()→uart_register_driver()→gprs_uart_probe()→uart_add_one_port()→gprs_uart_config_port()→gprs_uart_request_port()→request_mem_region()

    rmmod gprs_uart.ko→gprs_exit_module()→uart_unregister_driver()→gprs_uart_remove()→uart_remove_one_port()→gprs_uart_release_port()→release_mem_region()

    例子驱动中申请和释放IRQ资源的整个过程如下:

    open /dev/gprs_uart→gprs_uart_startup()→request_irq()

    close /dev/gprs_uart→gprs_uart_shutdown()→free_irq()

    想了解更详细的调用过程可以在驱动模块各函数头插入printk(KERN_DEBUG "%s\n", __FUNCTION__);并在函数尾插入printk(KERN_DEBUG "%s done\n", __FUNCTION__);

    下面是串口驱动例子和其GPRS测试程序源码下载地址:

    #include

    #include

    #include        /* printk() */

    #include          /* kmalloc() */

    #include            /* everything... */

    #include         /* error codes */

    #include         /* size_t */

    #include         /* O_ACCMODE */

    #include          /* cli(), *_flags */

    #include         /* copy_*_user */

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #include

    #define DEV_NAME            "gprs_uart"     /* 设备名 */

    /* 这里将串口的主设备号设为0,则串口设备编号由内核动态分配;你也可指定串口的设备编号 */

    #define GPRS_UART_MAJOR        0            /* 主设备号 */

    #define GPRS_UART_MINOR        0            /* 次设备号 */

    #define GPRS_UART_FIFO_SIZE    16           /* 串口FIFO的大小 */

    #define RXSTAT_DUMMY_READ    (0x10000000)

    #define MAP_SIZE             (0x100)        /* 要映射的串口IO内存区大小 */

    /* 串口发送中断号 */

    #define TX_IRQ(port) ((port)->irq + 1)

    /* 串口接收中断号 */

    #define RX_IRQ(port) ((port)->irq)

    /* 允许串口接收字符的标志 */

    #define tx_enabled(port) ((port)->unused[0])

    /* 允许串口发送字符的标志 */

    #define rx_enabled(port) ((port)->unused[1])

    /* 获取寄存器地址 */

    #define portaddr(port, reg) ((port)->membase + (reg))

    /* 读8位宽的寄存器 */

    #define rd_regb(port, reg) (ioread8(portaddr(port, reg)))

    /* 读32位宽的寄存器 */

    #define rd_regl(port, reg) (ioread32(portaddr(port, reg)))

    /* 写8位宽的寄存器 */

    #define wr_regb(port, reg, val) \

    do { iowrite8(val, portaddr(port, reg)); } while(0)

    /* 写32位宽的寄存器 */

    #define wr_regl(port, reg, val) \

    do { iowrite32(val, portaddr(port, reg)); } while(0)

    /* 禁止串口发送数据 */

    static void gprs_uart_stop_tx(struct uart_port *port)

    {

    if (tx_enabled(port))            /* 若串口已启动发送 */

    {

    disable_irq(TX_IRQ(port));   /* 禁止发送中断 */

    tx_enabled(port) = 0;        /* 设置串口为未启动发送 */

    }

    }

    /* 使能串口发送数据 */

    static void gprs_uart_start_tx(struct uart_port *port)

    {

    if (!tx_enabled(port))           /* 若串口未启动发送 */

    {

    enable_irq(TX_IRQ(port));    /* 使能发送中断 */

    tx_enabled(port) = 1;        /* 设置串口为已启动发送 */

    }

    }

    /* 禁止串口接收数据 */

    static void gprs_uart_stop_rx(struct uart_port *port)

    {

    if (rx_enabled(port))            /* 若串口已启动接收 */

    {

    disable_irq(RX_IRQ(port));   /* 禁止接收中断 */

    rx_enabled(port) = 0;        /* 设置串口为未启动接收 */

    }

    }

    /* 使能modem的状态信号 */

    static void gprs_uart_enable_ms(struct uart_port *port)

    {

    }

    /* 串口的Tx FIFO缓存是否为空 */

    static unsigned int gprs_uart_tx_empty(struct uart_port *port)

    {

    int ret = 1;

    unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);

    unsigned long ufcon = rd_regl(port, S3C2410_UFCON);

    if (ufcon & S3C2410_UFCON_FIFOMODE)    /* 若使能了FIFO */

    {

    if ((ufstat & S3C2410_UFSTAT_TXMASK) != 0 ||    /* 0 (ufstat & S3C2410_UFSTAT_TXFULL))       /* FIFO满 */

    ret = 0;

    }

    else    /* 若未使能了FIFO,则判断发送缓存和发送移位寄存器是否均为空 */

    {

    ret = rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE;

    }

    return ret;

    }

    /* 获取串口modem控制,因为uart2无modem控制,所以CTS、DSR直接返回有效 */

    static unsigned int gprs_uart_get_mctrl(struct uart_port *port)

    {

    return (TIOCM_CTS | TIOCM_DSR | TIOCM_CAR);

    }

    /* 设置串口modem控制 */

    static void gprs_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)

    {

    }

    /* 设置break信号 */

    static void gprs_uart_break_ctl(struct uart_port *port, int break_state)

    {

    unsigned long flags;

    unsigned int ucon;

    spin_lock_irqsave(&port->lock, flags);

    ucon = rd_regl(port, S3C2410_UCON);

    if (break_state)

    ucon |= S3C2410_UCON_SBREAK;

    else

    ucon &= ~S3C2410_UCON_SBREAK;

    wr_regl(port, S3C2410_UCON, ucon);

    spin_unlock_irqrestore(&port->lock, flags);

    }

    /* 返回Rx FIFO已存多少数据 */

    static int gprs_uart_rx_fifocnt(unsigned long ufstat)

    {

    /* 若Rx FIFO已满,返回FIFO的大小 */

    if (ufstat & S3C2410_UFSTAT_RXFULL)

    return GPRS_UART_FIFO_SIZE;

    /* 若FIFO未满,返回Rx FIFO已存了多少字节数据 */

    return (ufstat & S3C2410_UFSTAT_RXMASK) >> S3C2410_UFSTAT_RXSHIFT;

    }

    #define S3C2410_UERSTAT_PARITY (0x1000)

    /* 串口接收中断处理函数,获取串口接收到的数据,并将这些数据递交给行规则层 */

    static irqreturn_t gprs_uart_rx_chars(int irq, void *dev_id)

    {

    struct uart_port *port = dev_id;

    struct tty_struct *tty = port->info->tty;

    unsigned int ufcon, ch, flag, ufstat, uerstat;

    int max_count = 64;

    /* 循环接收数据,最多一次中断接收64字节数据 */

    while (max_count-- > 0)

    {

    ufcon = rd_regl(port, S3C2410_UFCON);

    ufstat = rd_regl(port, S3C2410_UFSTAT);

    /* 若Rx FIFO无数据了,跳出循环 */

    if (gprs_uart_rx_fifocnt(ufstat) == 0)

    break;

    /* 读取Rx error状态寄存器 */

    uerstat = rd_regl(port, S3C2410_UERSTAT);

    /* 读取已接受到的数据 */

    ch = rd_regb(port, S3C2410_URXH);

    /* insert the character into the buffer */

    /* 先将tty标志设为正常 */

    flag = TTY_NORMAL;

    /* 递增接收字符计数器 */

    port->icount.rx++;

    /* 判断是否存在Rx error

    * if (unlikely(uerstat & S3C2410_UERSTAT_ANY))等同于

    * if (uerstat & S3C2410_UERSTAT_ANY)

    * 只是unlikely表示uerstat & S3C2410_UERSTAT_ANY的值为假的可能性大一些

    * 另外还有一个likely(value)表示value的值为真的可能性更大一些

    */

    if (unlikely(uerstat & S3C2410_UERSTAT_ANY))

    {

    /* 若break错误,递增icount.brk计算器 */

    if (uerstat & S3C2410_UERSTAT_BREAK)

    {

    port->icount.brk++;

    if (uart_handle_break(port))

    goto ignore_char;

    }

    /* 若frame错误,递增icount.frame计算器 */

    if (uerstat & S3C2410_UERSTAT_FRAME)

    port->icount.frame++;

    /* 若overrun错误,递增icount.overrun计算器 */

    if (uerstat & S3C2410_UERSTAT_OVERRUN)

    port->icount.overrun++;

    /* 查看我们是否关心该Rx error

    * port->read_status_mask保存着我们感兴趣的Rx error status

    */

    uerstat &= port->read_status_mask;

    /* 若我们关心该Rx error,则将flag设置为对应的error flag */

    if (uerstat & S3C2410_UERSTAT_BREAK)

    flag = TTY_BREAK;

    else if (uerstat & S3C2410_UERSTAT_PARITY)

    flag = TTY_PARITY;

    else if (uerstat & ( S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))

    flag = TTY_FRAME;

    }

    /* 处理sys字符 */

    if (uart_handle_sysrq_char(port, ch))

    goto ignore_char;

    /* 将接收到的字符插入到tty设备的flip缓冲 */

    uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);

    ignore_char:

    continue;

    }

    /* 刷新tty设备的flip缓冲,将接受到的数据传给行规则层 */

    tty_flip_buffer_push(tty);

    return IRQ_HANDLED;

    }

    /* 串口发送中断处理函数,将用户空间的数据(保存在环形缓冲xmit里)发送出去 */

    static irqreturn_t gprs_uart_tx_chars(int irq, void *dev_id)

    {

    struct uart_port *port = dev_id;

    struct circ_buf *xmit = &port->info->xmit;        /* 获取环线缓冲 */

    int count = 256;

    /* 若设置了xChar字符 */

    if (port->x_char)

    {

    /* 将该xChar发送出去 */

    wr_regb(port, S3C2410_UTXH, port->x_char);

    /* 递增发送计数 */

    port->icount.tx++;

    /* 清除xChar */

    port->x_char = 0;

    /* 退出中断处理 */

    goto out;

    }

    /* 如果没有更多的字符需要发送(环形缓冲为空),

    * 或者uart Tx已停止,

    * 则停止uart并退出中断处理函数

    */

    if (uart_circ_empty(xmit) || uart_tx_stopped(port))

    {

    gprs_uart_stop_tx(port);

    goto out;

    }

    /* 循环发送数据,直到环形缓冲为空,最多一次中断发送256字节数据 */

    while (!uart_circ_empty(xmit) && count-- > 0)

    {

    /* 若Tx FIFO已满,退出循环 */

    if (rd_regl(port, S3C2410_UFSTAT) & S3C2410_UFSTAT_TXFULL)

    break;

    /* 将要发送的数据写入Tx FIFO */

    wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);

    /* 移向循环缓冲中下一要发送的数据 */

    xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);

    port->icount.tx++;

    }

    /* 如果环形缓冲区中剩余的字符少于WAKEUP_CHARS,唤醒上层 */

    if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)

    uart_write_wakeup(port);

    /* 如果环形缓冲为空,则停止发送 */

    if (uart_circ_empty(xmit))

    gprs_uart_stop_tx(port);

    out:

    return IRQ_HANDLED;

    }

    /* 启动串口端口,在打开该驱动的设备文件时会调用该函数来申请串口中断,并设置串口为可接受,也可发送 */

    static int gprs_uart_startup(struct uart_port *port)

    {

    unsigned long flags;

    int ret;

    const char *portname = to_platform_device(port->dev)->name;

    /* 设置串口为不可接受,也不可发送 */

    rx_enabled(port) = 0;

    tx_enabled(port) = 0;

    spin_lock_irqsave(&port->lock, flags);

    /* 申请接收中断 */

    ret = request_irq(RX_IRQ(port), gprs_uart_rx_chars, 0, portname, port);

    if (ret != 0)

    {

    printk(KERN_ERR "cannot get irq %d\n", RX_IRQ(port));

    return ret;

    }

    /* 设置串口为允许接收 */

    rx_enabled(port) = 1;

    /* 申请发送中断 */

    ret = request_irq(TX_IRQ(port), gprs_uart_tx_chars, 0, portname, port);

    if (ret)

    {

    printk(KERN_ERR "cannot get irq %d\n", TX_IRQ(port));

    rx_enabled(port) = 0;

    free_irq(RX_IRQ(port), port);

    goto err;

    }

    /* 设置串口为允许发送 */

    tx_enabled(port) = 1;

    err:

    spin_unlock_irqrestore(&port->lock, flags);

    return ret;

    }

    /* 关闭串口,在关闭驱动的设备文件时会调用该函数,释放串口中断 */

    static void gprs_uart_shutdown(struct uart_port *port)

    {

    rx_enabled(port) = 0;                /* 设置串口为不允许接收    */

    free_irq(RX_IRQ(port), port);        /* 释放接收中断    */

    tx_enabled(port) = 0;                /* 设置串口为不允许发送    */

    free_irq(TX_IRQ(port), port);        /* 释放发送中断    */

    }

    /* 设置串口参数 */

    static void gprs_uart_set_termios(struct uart_port *port,

    struct ktermios *termios,

    struct ktermios *old)

    {

    unsigned long flags;

    unsigned int baud, quot;

    unsigned int ulcon, ufcon = 0;

    /* 不支持moden控制信号线

    * HUPCL:    关闭时挂断moden

    * CMSPAR:    mark or space (stick) parity

    * CLOCAL:    忽略任何moden控制线

    */

    termios->c_cflag &= ~(HUPCL | CMSPAR);

    termios->c_cflag |= CLOCAL;

    /* 获取用户设置的串口波特率,并计算分频数(串口波特率除数quot) */

    baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);

    if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)

    quot = port->custom_divisor;

    else

    quot = port->uartclk / baud / 16 - 1;

    /* 设置数据字长 */

    switch (termios->c_cflag & CSIZE)

    {

    case CS5:

    ulcon = S3C2410_LCON_CS5;

    break;

    case CS6:

    ulcon = S3C2410_LCON_CS6;

    break;

    case CS7:

    ulcon = S3C2410_LCON_CS7;

    break;

    case CS8:

    default:

    ulcon = S3C2410_LCON_CS8;

    break;

    }

    /* 是否要求设置两个停止位(CSTOPB) */

    if (termios->c_cflag & CSTOPB)

    ulcon |= S3C2410_LCON_STOPB;

    /* 是否使用奇偶检验 */

    if (termios->c_cflag & PARENB)

    {

    if (termios->c_cflag & PARODD)  /* 奇校验 */

    ulcon |= S3C2410_LCON_PODD;

    else                            /* 偶校验 */

    ulcon |= S3C2410_LCON_PEVEN;

    }

    else                                /* 无校验 */

    {

    ulcon |= S3C2410_LCON_PNONE;

    }

    if (port->fifosize > 1)

    ufcon |= S3C2410_UFCON_FIFOMODE | S3C2410_UFCON_RXTRIG8;

    spin_lock_irqsave(&port->lock, flags);

    /* 设置FIFO控制寄存器、线控制寄存器和波特率除数寄存器 */

    wr_regl(port, S3C2410_UFCON, ufcon);

    wr_regl(port, S3C2410_ULCON, ulcon);

    wr_regl(port, S3C2410_UBRDIV, quot);

    /* 更新串口FIFO的超时时限 */

    uart_update_timeout(port, termios->c_cflag, baud);

    /* 设置我们感兴趣的Rx error */

    port->read_status_mask = S3C2410_UERSTAT_OVERRUN;

    if (termios->c_iflag & INPCK)

    port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;

    /* 设置我们忽略的Rx error */

    port->ignore_status_mask = 0;

    if (termios->c_iflag & IGNPAR)

    port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;

    if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)

    port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;

    /* 若未设置CREAD(使用接收器),则忽略所有Rx error*/

    if ((termios->c_cflag & CREAD) == 0)

    port->ignore_status_mask |= RXSTAT_DUMMY_READ;

    spin_unlock_irqrestore(&port->lock, flags);

    }

    /* 获取串口类型 */

    static const char *gprs_uart_type(struct uart_port *port)

    {/* 返回描述串口类型的字符串指针 */

    return port->type == PORT_S3C2410 ? "gprs_uart:s3c2410_uart2" : NULL;

    }

    /* 申请串口一些必要的资源,如IO端口/IO内存资源,必要时还可以重新映射串口端口 */

    static int gprs_uart_request_port(struct uart_port *port)

    {

    struct resource *res;

    const char *name = to_platform_device(port->dev)->name;

    /* request_mem_region请求分配IO内存,从开始port->mapbase,大小MAP_SIZE

    * port->mapbase保存当前串口的寄存器基地址(物理)

    * uart2: 0x50008000

    */

    res = request_mem_region(port->mapbase, MAP_SIZE, name);

    if (res == NULL)

    {

    printk(KERN_ERR"request_mem_region error: %p\n", res);

    return -EBUSY;

    }

    return 0;

    }

    /* 释放串口已申请的IO端口/IO内存资源,必要时还需iounmap */

    static void gprs_uart_release_port(struct uart_port *port)

    {

    /* 释放已分配IO内存 */

    release_mem_region(port->mapbase, MAP_SIZE);

    }

    /* 执行串口所需的自动配置 */

    static void gprs_uart_config_port(struct uart_port *port, int flags)

    {

    int retval;

    /* 请求串口 */

    retval = gprs_uart_request_port(port);

    /* 设置串口类型 */

    if (flags & UART_CONFIG_TYPE && retval == 0)

    port->type = PORT_S3C2410;

    }

    /* The UART operations structure */

    static struct uart_ops gprs_uart_ops = {

    .start_tx        = gprs_uart_start_tx,      /* Start transmitting */

    .stop_tx        = gprs_uart_stop_tx,        /* Stop transmission */

    .stop_rx        = gprs_uart_stop_rx,        /* Stop reception */

    .enable_ms        = gprs_uart_enable_ms,    /* Enable modem status signals */

    .tx_empty        = gprs_uart_tx_empty,      /* Transmitter busy? */

    .get_mctrl        = gprs_uart_get_mctrl,    /* Get modem control */

    .set_mctrl        = gprs_uart_set_mctrl,    /* Set modem control */

    .break_ctl        = gprs_uart_break_ctl,    /* Set break signal */

    .startup        = gprs_uart_startup,        /* App opens GPRS_UART */

    .shutdown        = gprs_uart_shutdown,      /* App closes GPRS_UART */

    .set_termios    = gprs_uart_set_termios,    /* Set termios */

    .type            = gprs_uart_type,          /* Get UART type */

    .request_port    = gprs_uart_request_port,  /* Claim resources associated with a GPRS_UART port */

    .release_port    = gprs_uart_release_port,  /* Release resources associated with a GPRS_UART port */

    .config_port    = gprs_uart_config_port,    /* Configure when driver adds a GPRS_UART port */

    };

    /* Uart driver for GPRS_UART */

    static struct uart_driver gprs_uart_driver = {

    .owner = THIS_MODULE,                /* Owner */

    .driver_name = DEV_NAME,             /* Driver name */

    .dev_name = DEV_NAME,                /* Device node name */

    .major = GPRS_UART_MAJOR,            /* Major number */

    .minor = GPRS_UART_MINOR,            /* Minor number start */

    .nr = 1,                             /* Number of UART ports */

    };

    /* Uart port for GPRS_UART port */

    static struct uart_port gprs_uart_port = {

    .irq        = IRQ_S3CUART_RX2,           /* IRQ */

    .fifosize    = GPRS_UART_FIFO_SIZE,      /* Size of the FIFO */

    .iotype        = UPIO_MEM,               /* IO memory */

    .flags        = UPF_BOOT_AUTOCONF,       /* UART port flag */

    .ops        = &gprs_uart_ops,            /* UART operations */

    .line        = 0,                        /* UART port number */

    .lock        = __SPIN_LOCK_UNLOCKED(gprs_uart_port.lock),

    };

    /* 初始化指定串口端口 */

    static int gprs_uart_init_port(struct uart_port *port, struct platform_device *platdev)

    {

    unsigned long flags;

    unsigned int gphcon;

    if (platdev == NULL)

    return -ENODEV;

    port->dev        = &platdev->dev;

    /* 设置串口波特率时钟频率 */

    port->uartclk    = clk_get_rate(clk_get(&platdev->dev, "pclk"));

    /* 设置串口的寄存器基地址(物理): 0x50008000 */

    port->mapbase    = S3C2410_PA_UART2;

    /* 设置当前串口的寄存器基地址(虚拟): 0xF5008000 */

    port->membase    = S3C24XX_VA_UART + (S3C2410_PA_UART2 - S3C24XX_PA_UART);

    spin_lock_irqsave(&port->lock, flags);

    wr_regl(port, S3C2410_UCON, S3C2410_UCON_DEFAULT);

    wr_regl(port, S3C2410_ULCON, S3C2410_LCON_CS8 | S3C2410_LCON_PNONE);

    wr_regl(port, S3C2410_UFCON, S3C2410_UFCON_FIFOMODE

    | S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_RESETBOTH);

    /* 将I/O port H的gph6和gph7设置为TXD2和RXD2 */

    gphcon = readl(S3C2410_GPHCON);

    gphcon &= ~((0x5) << 12);

    writel(gphcon, S3C2410_GPHCON);

    spin_unlock_irqrestore(&port->lock, flags);

    return 0;

    }

    /* Platform driver probe */

    static int __init gprs_uart_probe(struct platform_device *dev)

    {

    int ret;

    /* 初始化串口 */

    ret = gprs_uart_init_port(&gprs_uart_port, dev);

    if (ret < 0)

    {

    printk(KERN_ERR"gprs_uart_probe: gprs_uart_init_port error: %d\n", ret);

    return ret;

    }

    /* 添加串口 */

    ret = uart_add_one_port(&gprs_uart_driver, &gprs_uart_port);

    if (ret < 0)

    {

    printk(KERN_ERR"gprs_uart_probe: uart_add_one_port error: %d\n", ret);

    return ret;

    }

    /* 将串口uart_port结构体保存在platform_device->dev->driver_data中 */

    platform_set_drvdata(dev, &gprs_uart_port);

    return 0;

    }

    /* Called when the platform driver is unregistered */

    static int gprs_uart_remove(struct platform_device *dev)

    {

    platform_set_drvdata(dev, NULL);

    /* 移除串口 */

    uart_remove_one_port(&gprs_uart_driver, &gprs_uart_port);

    return 0;

    }

    /* Suspend power management event */

    static int gprs_uart_suspend(struct platform_device *dev, pm_message_t state)

    {

    uart_suspend_port(&gprs_uart_driver, &gprs_uart_port);

    return 0;

    }

    /* Resume after a previous suspend */

    static int gprs_uart_resume(struct platform_device *dev)

    {

    uart_resume_port(&gprs_uart_driver, &gprs_uart_port);

    return 0;

    }

    /* Platform driver for GPRS_UART */

    static struct platform_driver gprs_plat_driver = {

    .probe = gprs_uart_probe,                /* Probe method */

    .remove = __exit_p(gprs_uart_remove),    /* Detach method */

    .suspend = gprs_uart_suspend,            /* Power suspend */

    .resume = gprs_uart_resume,              /* Resume after a suspend */

    .driver = {

    .owner    = THIS_MODULE,

    .name = DEV_NAME,                    /* Driver name */

    },

    };

    /* Platform device for GPRS_UART */

    struct platform_device *gprs_plat_device;

    static int __init gprs_init_module(void)

    {

    int retval;

    /* Register uart_driver for GPRS_UART */

    retval = uart_register_driver(&gprs_uart_driver);

    if (0 != retval)

    {

    printk(KERN_ERR "gprs_init_module: can't register the GPRS_UART driver %d\n", retval);

    return retval;

    }

    /* Register platform device for GPRS_UART. Usually called

    during architecture-specific setup */

    gprs_plat_device = platform_device_register_simple(DEV_NAME, 0, NULL, 0);

    if (IS_ERR(gprs_plat_device))

    {

    retval = PTR_ERR(gprs_plat_device);

    printk(KERN_ERR "gprs_init_module: can't register platform device %d\n", retval);

    goto fail_reg_plat_dev;

    }

    /* Announce a matching driver for the platform

    devices registered above */

    retval = platform_driver_register(&gprs_plat_driver);

    if (0 != retval)

    {

    printk(KERN_ERR "gprs_init_module: can't register platform driver %d\n", retval);

    goto fail_reg_plat_drv;

    }

    return 0; /* succeed */

    fail_reg_plat_drv:

    platform_device_unregister(gprs_plat_device);

    fail_reg_plat_dev:

    uart_unregister_driver(&gprs_uart_driver);

    return retval;

    }

    static void __exit gprs_exit_module(void)

    {

    /* The order of unregistration is important. Unregistering the

    UART driver before the platform driver will crash the system */

    /* Unregister the platform driver */

    platform_driver_unregister(&gprs_plat_driver);

    /* Unregister the platform devices */

    platform_device_unregister(gprs_plat_device);

    /* Unregister the GPRS_UART driver */

    uart_unregister_driver(&gprs_uart_driver);

    }

    module_init(gprs_init_module);

    module_exit(gprs_exit_module);

    MODULE_AUTHOR("lingd");

    MODULE_LICENSE("Dual BSD/GPL");

    展开全文
    weixin_34999698 2021-05-12 15:45:53
  • Qt 打开串口时可能会失败,因为操作驱动设备需要 root 权限,为此,解决方法有两种: 1 把对应的串口设备文件的所有权和组改成普通用户的,如:sudo chown xxx:xxx /dev/ttyF0 2 把当前用户的组类别改成 dialout...

    Qt 对串口的操作封装在了 QSerialPort 中,关于它的具体函数及使用方法参考 这篇文章

    1 读取串口

    • 关于设备权限的问题

      Qt 打开串口时可能会失败,因为操作驱动设备需要 root 权限,为此,解决方法有两种:

      • 1 把对应的串口设备文件的所有权和组改成普通用户的,如:sudo chown xxx:xxx /dev/ttyF0
      • 2 把当前用户的组类别改成 dialout(串口 tty 设备所在组为 dialout 组),命令:sudo gpasswd -a $USER dialout
    • 在.pro文件中进行声明

    QT += serialport
    
    • 头文件
    #include <QSerialPort>
    #include <QSerialPortInfo>
    //#include <QDebug>
    
    • 实例化 QSrerialPort
    QSerialPort *serial=new QSerialPort
    
    • 寻找显示目前的所有串口信息
       foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
        {
            qDebug() << "Name : " << info.portName();
            qDebug() << "Description : " << info.description();
            qDebug() << "Manufacturer: " << info.manufacturer();
            qDebug() << "Serial Number: " << info.serialNumber();
            qDebug() << "System Location: " << info.systemLocation();
        }
    
    • 打开(关闭)串口
        serial->setPortName("ttyF1"); // 串口名称
        serial->open(QIODevice::ReadOnly); // 打开失败会返回 false
        // serial->clear(); //清除缓存区
        // serial->close(); //关闭串口
    
    • 配置参数
        s->setBaudRate(QSerialPort::Baud4800);
        s->setParity(QSerialPort::NoParity);
        s->setStopBits(QSerialPort::OneStop);
        //s->setFlowControl(QSerialPort::NoFlowControl);
        s->setFlowControl(QSerialPort::HardwareControl);
        s->setDataBits(QSerialPort::Data8);
    
    • 使用信号与槽读取串口
    /***** 在头文件中声明该槽函数
    private slots:
        void ReadData();
    *******/ 
    /***** 连接信号与槽
    connect(s,&QSerialPort::readyRead,this,&MainWindow::ReadData);
    ******/
    void MainWindow::ReadData()
    {
        QByteArray buf;
        buf=s->readAll();
        if(!buf.isEmpty()) {
            QString str = buf;
            //std::cout  << str.toStdString() << std::endl;
            std::cout  << str.data() << std::endl;
            //qDebug()  << str;
        }
        buf.clear();
    }
    

    2 读取串口各管脚信号

    串口的接头有两种,下图为“上公下母”:

    以图中的“公头”为例,其各个管脚的含义:

    管脚简写含义输入/输出
    1DCD数据载波检测input
    2RxD接收数据input
    3TxD发送数据output
    4DTR数据终端准备output
    5GND
    6DSR数据设备准备好input
    7RTS请求发送output
    8CTS清除发送input
    9RI震铃指示input

    如果要读取串口某个管脚的信号,可以使用 QSerialPort::PinoutSignals PinoutSignals(),该函数以位图的格式返回引脚信号状态;具体的返回值请查看 QSerialPort::PinoutSignals 的枚举值;简单的使用例子:

    QSerialPort::PinoutSignals m = s->pinoutSignals();
    if (m & QSerialPort::ClearToSendSignal)
        qDebug() << " CTS ";
    if (m & QSerialPort::DataSetReadySignal)
        qDebug() << " DSR ";
    

    3 Ubuntu 串口调试助手

    • 安装
      sudo apt-get install cutecom
      
    • 打开
      cutecom
      
      
      
    展开全文
    qq_33236581 2021-02-02 12:41:41
  • 三、串口的打开在用户空间执行open操作的时候,就会执行uart_ops->open. Uart_ops的定义如下:tty_open=>init_dev=>initialize_tty_struct=>tty_ldisc_assign=>将tty_ldisc_N_TTY复制给该dev然后tty-&...

    三、串口的打开

    在用户空间执行open操作的时候,就会执行uart_ops->open. Uart_ops的定义如下:

    tty_open=>init_dev=>initialize_tty_struct=>tty_ldisc_assign=>

    将tty_ldisc_N_TTY复制给该dev

    然后tty->driver->open(tty, filp);

    tty->driver为上面uart_register_driver时注册的tty_driver驱动,它的操作方法集为uart_ops.

    tty_fops.tty_open=>

    tty->driver->open就是uart_ops.uart_open=>uart_startup=>

    port->ops->startup(port)这里port的ops就是serial_pxa_pops;也这就是该物理uart口,struct uart_port的操作函数

    serial_pxa_pops.startup就是serial_pxa_startup

    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,

    .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

    .read_proc    = uart_read_proc,

    #endif

    .tiocmget = uart_tiocmget,

    .tiocmset = uart_tiocmset,

    };

    对应open的操作接口为uart_open.代码如下:

    static int uart_open(struct tty_struct *tty, struct file *filp)

    {

    struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;

    struct uart_state *state;

    int retval, line = tty->index;

    BUG_ON(!kernel_locked());

    pr_debug("uart_open(%d) called\n", line);

    /*

    * tty->driver->num won't change, so we won't fail here with

    * tty->driver_data set to something non-NULL (and therefore

    * we won't get caught by uart_close()).

    */

    retval = -ENODEV;

    if (line >= tty->driver->num)

    goto fail;

    /*

    * We take the semaphore inside uart_get to guarantee that we won't

    * be re-entered while allocating the info structure, or while we

    * request any IRQs that the driver may need.  This also has the nice

    * side-effect that it delays the action of uart_hangup, so we can

    * guarantee that info->tty will always contain something reasonable.

    */

    state = uart_get(drv, line);

    if (IS_ERR(state)) {

    retval = PTR_ERR(state);

    goto fail;

    }

    /*

    * Once we set tty->driver_data here, we are guaranteed that

    * uart_close() will decrement the driver module use count.

    * Any failures from here onwards should not touch the count.

    */

    tty->driver_data = state;

    tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0;

    tty->alt_speed = 0;

    state->info->tty = tty;

    /*

    * If the port is in the middle of closing, bail out now.

    */

    if (tty_hung_up_p(filp)) {

    retval = -EAGAIN;

    state->count--;

    mutex_unlock(&state->mutex);

    goto fail;

    }

    /*

    * Make sure the device is in D0 state.

    */

    if (state->count == 1)

    uart_change_pm(state, 0);

    /*

    * Start up the serial port.

    */

    retval = uart_startup(state, 0);

    /*

    * If we succeeded, wait until the port is ready.

    */

    if (retval == 0)

    retval = uart_block_til_ready(filp, state);

    mutex_unlock(&state->mutex);

    /*

    * If this is the first open to succeed, adjust things to suit.

    */

    if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) {

    state->info->flags |= UIF_NORMAL_ACTIVE;

    uart_update_termios(state);

    }

    fail:

    return retval;

    }

    在这里函数里,继续完成操作的设备文件所对应state初始化.现在用户空间open这个设备了.即要对这个文件进行操作了.那uart_port也要开始工作了.即调用uart_startup()使其进入工作状态.当然,也需要初始化uart_port所对应的环形缓冲区circ_buf.即state->info-> xmit.

    特别要注意,在这里将tty->driver_data = state;这是因为以后的操作只有port相关了,不需要去了解uart_driver的相关信息.

    跟踪看一下里面调用的两个重要的子函数. uart_get()和uart_startup().先分析uart_get().代码如下:

    static struct uart_state *uart_get(struct uart_driver *drv, int line)

    {

    struct uart_state *state;

    int ret = 0;

    state = drv->state + line;

    if (mutex_lock_interruptible(&state->mutex)) {

    ret = -ERESTARTSYS;

    goto err;

    }

    state->count++;

    if (!state->port || state->port->flags & UPF_DEAD) {

    ret = -ENXIO;

    goto err_unlock;

    }

    if (!state->info) {

    state->info = kzalloc(sizeof(struct uart_info), GFP_KERNEL);

    if (state->info) {

    init_waitqueue_head(&state->info->open_wait);

    init_waitqueue_head(&state->info->delta_msr_wait);

    /*

    * Link the info into the other structures.

    */

    state->port->info = state->info;

    tasklet_init(&state->info->tlet, uart_tasklet_action,

    (unsigned long)state);

    } else {

    ret = -ENOMEM;

    goto err_unlock;

    }

    }

    return state;

    err_unlock:

    state->count--;

    mutex_unlock(&state->mutex);

    err:

    return ERR_PTR(ret);

    }

    //从代码中可以看出.这里注要是操作是初始化state->info.注意port->info就是state->info的一个副本.即port直接通过port->info可以找到它要操作的缓存区.

    //uart_startup()代码如下:

    static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw)

    {

    struct uart_port *uport = state->uart_port;

    struct tty_port *port = &state->port;

    unsigned long page;

    int retval = 0;

    if (port->flags & ASYNC_INITIALIZED)

    return 0;

    /*

    * Set the TTY IO error marker - we will only clear this

    * once we have successfully opened the port.  Also set

    * up the tty->alt_speed kludge

    */

    set_bit(TTY_IO_ERROR, &tty->flags);

    if (uport->type == PORT_UNKNOWN)

    return 0;

    /*

    * Initialise and allocate the transmit and temporary

    * buffer.

    */

    if (!state->xmit.buf) {

    /* This is protected by the per port mutex */

    page = get_zeroed_page(GFP_KERNEL);

    if (!page)

    return -ENOMEM;

    state->xmit.buf = (unsigned char *) page;

    uart_circ_clear(&state->xmit);

    }

    //在这里,注要完成对环形缓冲,即info->xmit的初始化.然后调用port->ops->startup( )将这个port带入到工作状态.

    retval = uport->ops->startup(uport);//调用8250.c中的serial8250_startup()函数

    if (retval == 0) {

    if (init_hw) {

    /*

    * Initialise the hardware port settings.

    */

    uart_change_speed(tty, state, NULL);

    /*

    * Setup the RTS and DTR signals once the

    * port is open and ready to respond.

    */

    if (tty->termios->c_cflag & CBAUD)

    uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);

    }

    if (port->flags & ASYNC_CTS_FLOW) {

    spin_lock_irq(&uport->lock);

    if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))

    tty->hw_stopped = 1;

    spin_unlock_irq(&uport->lock);

    }

    set_bit(ASYNCB_INITIALIZED, &port->flags);

    clear_bit(TTY_IO_ERROR, &tty->flags);

    }

    if (retval && capable(CAP_SYS_ADMIN))

    retval = 0;

    return retval;

    }

    static int serial8250_startup(struct uart_port *port)

    {

    struct uart_8250_port *up = (struct uart_8250_port *)port;

    unsigned long flags;

    unsigned char lsr, iir;

    int retval;

    //从结构体uart_config中取得相应的配置

    up->capabilities = uart_config[up->port.type].flags;

    up->mcr = 0;

    if (up->port.type == PORT_16C950) {  //这里我们没有调用

    ……………………

    }

    #ifdef CONFIG_SERIAL_8250_RSA

    enable_rsa(up);

    #endif

    //清楚FIFO  buffers并 disable 他们,但会在以后set_termios()函数中,重新使能他们

    serial8250_clear_fifos(up);

    //复位LSR,RX,IIR,MSR寄存器

    (void) serial_inp(up, UART_LSR);

    (void) serial_inp(up, UART_RX);

    (void) serial_inp(up, UART_IIR);

    (void) serial_inp(up, UART_MSR);

    //若LSR寄存器中的值为0xFF.异常

    if (!(up->port.flags & UPF_BUGGY_UART) &&(serial_inp(up, UART_LSR) == 0xff)) {

    printk("ttyS%d: LSR safety check engaged!\n", up->port.line);

    return -ENODEV;

    }

    //16850系列芯片的处理,忽略

    if (up->port.type == PORT_16850) {

    ………………………………………………

    }

    if (is_real_interrupt(up->port.irq)) {

    /*

    * Test for UARTs that do not reassert THRE when the

    * transmitter is idle and the interrupt has already

    * been cleared.  Real 16550s should always reassert

    * this interrupt whenever the transmitter is idle and

    * the interrupt is enabled.  Delays are necessary to

    * allow register changes to become visible.

    */

    spin_lock_irqsave(&up->port.lock, flags);

    wait_for_xmitr(up, UART_LSR_THRE);

    serial_out_sync(up, UART_IER, UART_IER_THRI);

    udelay(1); /* allow THRE to set */

    serial_in(up, UART_IIR);

    serial_out(up, UART_IER, 0);

    serial_out_sync(up, UART_IER, UART_IER_THRI);

    udelay(1); /* allow a working UART time to re-assert THRE */

    iir = serial_in(up, UART_IIR);

    serial_out(up, UART_IER, 0);

    spin_unlock_irqrestore(&up->port.lock, flags);

    /*

    * If the interrupt is not reasserted, setup a timer to

    * kick the UART on a regular basis.

    */

    if (iir & UART_IIR_NO_INT) {

    pr_debug("ttyS%d - using backup timer\n", port->line);

    up->timer.function = serial8250_backup_timeout;

    up->timer.data = (unsigned long)up;

    mod_timer(&up->timer, jiffies +poll_timeout(up->port.timeout) + HZ/5);

    }

    }

    //如果中断号有效,还要进一步判断这个中断号是否有效.具体操作为,先等待8250发送寄存器空.然后允许发送中断空的中断.然后判断IIR寄存器是否收到中断.

    //如果有没有收到中断,则说明这根中断线无效.只能采用轮询的方式.关于轮询方式,我们在之后再以独立章节的形式给出分析

    if (!is_real_interrupt(up->port.irq)) {

    up->timer.data = (unsigned long)up;

    mod_timer(&up->timer, jiffies + poll_timeout(up->port.timeout));

    } else {

    retval = serial_link_irq_chain(up);//定义串口的中断函数

    if (retval)

    return retval;

    }

    //如果没有设置中断号,则采用轮询方式;如果中断后有效.流程转入serial_link_irq_chain().在这个里面.会注册中断处理函数

    serial_outp(up, UART_LCR, UART_LCR_WLEN8);  //ULCR.WLS=11,即选择8位

    spin_lock_irqsave(&up->port.lock, flags);

    if (up->port.flags & UPF_FOURPORT) {

    if (!is_real_interrupt(up->port.irq))

    up->port.mctrl |= TIOCM_OUT1;

    }

    else

    {

    //Most PC uarts need OUT2 raised to enable interrupts.

    if (is_real_interrupt(up->port.irq))

    up->port.mctrl |= TIOCM_OUT2;

    }

    serial8250_set_mctrl(&up->port, up->port.mctrl);

    /*

    * Do a quick test to see if we receive an

    * interrupt when we enable the TX irq.

    */

    serial_outp(up, UART_IER, UART_IER_THRI);

    lsr = serial_in(up, UART_LSR);

    iir = serial_in(up, UART_IIR);

    serial_outp(up, UART_IER, 0);

    if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {

    if (!(up->bugs & UART_BUG_TXEN)) {

    up->bugs |= UART_BUG_TXEN;

    pr_debug("ttyS%d - enabling bad tx status workarounds\n",port->line);

    }

    } else {

    up->bugs &= ~UART_BUG_TXEN;

    }

    spin_unlock_irqrestore(&up->port.lock, flags);

    /*

    * Finally, enable interrupts.  Note: Modem status interrupts

    * are set via set_termios(), which will be occurring imminently

    * anyway, so we don't enable them here.

    */

    up->ier = UART_IER_RLSI | UART_IER_RDI;

    serial_outp(up, UART_IER, up->ier);

    if (up->port.flags & UPF_FOURPORT) {

    unsigned int icp;

    //Enable interrupts on the AST Fourport board

    icp = (up->port.iobase & 0xfe0) | 0x01f;

    outb_p(0x80, icp);

    (void) inb_p(icp);

    }

    /*

    * And clear the interrupt registers again for luck.

    */

    (void) serial_inp(up, UART_LSR);

    (void) serial_inp(up, UART_RX);

    (void) serial_inp(up, UART_IIR);

    (void) serial_inp(up, UART_MSR);

    return 0;

    }

    四、串口的读

    tty_driver中并末提供read接口.上层的read操作是直接到ldsic的缓存区中读数据的.那ldsic的数据是怎么送入进去的呢?继续看中断处理中的数据接收流程.即为: receive_chars().代码片段如下:

    //这个应该是UART接受数据的函数uart_port结构定义在serial——core.h中

    //port中断函数serial8250_handle_port()调用这个函数:

    static void receive_chars(struct uart_8250_port *up, unsigned int *status)

    {

    ……

    ……

    uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);

    }

    //最后流据会转入uart_inset_char().这个函数是uart层提供的一个接口,代码如下:

    static inline void uart_insert_char(struct uart_port *port, unsigned int status,unsigned int overrun, unsigned int ch, unsigned int flag)

    {

    struct tty_struct *tty = port->info->tty;

    if ((status & port->ignore_status_mask & ~overrun) == 0)

    tty_insert_flip_char(tty, ch, flag);

    /*

    * Overrun is special.  Since it's reported immediately,

    * it doesn't affect the current character.

    */

    if (status & ~port->ignore_status_mask & overrun)

    tty_insert_flip_char(tty, 0, TTY_OVERRUN);

    }

    //tty_insert_filp()函数数据就直接交给了ldisc.

    读数据时,read()--->调用tty_io.c中的tty_read()--->n_tty.c中的 n_tty_read(),n_tty_read()从ldisc中读取数据。

    五、串口的写

    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,

    };

    写数据时,write()--->调用tty_io.c中的 tty_write()--->调用n_tty.c中的 n_tty_write()--->调用serial_core.c中 uart_write()--->调用serial_core.c中 uart_start()--->__uart_start()--->

    调用serial_core.c中 uart_send_xchar()--->调用8250.c中的写出函数serial8250_start_tx()--->调用8250.c中的transmit_chars()--->调用8250.c中的serial_outp()--->调用8250.c中的mem_serial_out()写出去

    展开全文
    weixin_28691003 2021-05-13 22:27:24
  • shenxingxin 2021-10-16 13:47:24
  • fengweibo112 2021-07-29 14:40:10
  • weixin_28756021 2021-05-11 05:15:27
  • weixin_36397104 2021-05-12 01:38:56
  • Alex88389516 2021-07-29 22:11:39
  • weixin_42297562 2020-12-24 01:15:41
  • weixin_39714015 2021-05-11 00:14:48
  • weixin_35067558 2020-12-24 01:14:25
  • weixin_39781186 2021-06-02 14:23:27
  • qq_42330920 2021-04-12 11:10:27
  • qq_35761570 2021-05-20 19:31:55
  • weixin_42500720 2021-05-15 09:07:35
  • daocaokafei 2021-02-04 20:36:28
  • weixin_35607952 2021-05-13 06:51:04
  • QQ3200028121 2021-09-29 13:20:07
  • xqhrs232 2020-12-24 14:41:47
  • weixin_32589453 2021-05-15 07:49:45
  • weixin_29589357 2021-05-13 23:52:06
  • wangjie36 2021-08-27 15:53:39
  • weixin_42362885 2021-05-14 03:44:41
  • weixin_41452575 2021-06-17 14:28:06
  • weixin_40407893 2021-06-16 14:01:41
  • weixin_33241808 2021-05-15 09:24:20
  • wuzhikaidetb 2021-10-13 21:29:19
  • John_chaos 2021-11-08 10:50:13
  • weixin_36168972 2021-05-11 14:19:00

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 37,336
精华内容 14,934
关键字:

驱动读写串口信息