linux串口驱动分析

stm32入门篇---USART配置和 printf方法 stm32 USART 配置 网上USART配置的博文很多,以前参考的时候也受益匪浅。最近在弄了一个蓝牙通讯控制的实验后就想回顾了一下自己的配置历程,来总结一个通俗易懂人性化的步骤讲解希望日后回顾和对初试者有帮助。(也可直接参考正点原子的例程) 中断通讯方式: 步骤一:根据电路原理图连接电路,交叉接线:TX----RX RX—TX ,同时接好Vcc和Gen 注意选择的是USART1 还是23... 阅读详情

linux串口驱动分析

标签: linuxstructccharacterbufferresources
29272人阅读 评论(1) 收藏 举报
本文章已收录于:
linux串口驱动分析



  • 硬件资源及描述
        s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)端口,每个端口都可以在中断模式或 DMA 模式下操作。UART 使用系统时钟可以支持最高 115.2Kbps 的波特率。每个 UART 通道对于接收器和发送器包括了 2 个 64 位的 FIFO。

寄存器名称地址在linux中的描述 (2410 和 2440 处理器对内存地址映射关系相同)
UART 线性控制寄存器(ULCONn)ULCON0 
ULCON1 
ULCON2 
0x50000000
0x50004000
0x50008000
linux-2.6.31/arch/arm/plat-s3c/include/plat/regs-serial.h

#define S3C2410_PA_UART0      

    (S3C24XX_PA_UART)

#define S3C2410_PA_UART1     

    (S3C24XX_PA_UART + 0x4000 )

#define S3C2410_PA_UART2     

    (S3C24XX_PA_UART + 0x8000 )

#define S3C2443_PA_UART3     

    (S3C24XX_PA_UART + 0xC000 )

linux-2.6.31/arch/arm/plat-s3c/include/plat/map.h

#define S3C24XX_VA_UART      

    S3C_VA_UART//静态映射后的虚拟地址

#define S3C2410_PA_UART      

    (0x50000000)//物理地址

#define S3C24XX_SZ_UART       SZ_1M
#define S3C_UART_OFFSET       (0x4000)
UART 控制寄存器(UCONn)UCON0 
UCON1
UCON2
0x50000004
0x50004004
0x50008004
#define S3C2410_UCON      (0x04)
UART FIFO 控制寄存器(UFCONn)UFCON0
UFCON1
UFCON2
0x50000008
0x50004008
0x50008008
#define S3C2410_UFCON      (0x08)
UART MODEM 控制寄存器(UMCONn)UMCON0
UMCON1
0x5000000C
0x5000400C
#define S3C2410_UMCON      (0x0C)
UART 接收发送状态寄存器(UTRSTATn)UTRSTAT0
UTRSTAT1
UTRSTAT2
0x50000010
0x50004010
0x50008010
#define S3C2410_UTRSTAT      (0x10)
UART 错误状态寄存器(UERSTATn)UERSTAT0
UERSTAT1
UERSTAT2
0x50000014
0x50004014
0x50008014
#define S3C2410_UERSTAT      (0x14)
UART FIFO 状态寄存器(UFSTATn)UFSTAT0
UFSTAT1
UFSTAT2
0x50000018
0x50004018
0x50008018
#define S3C2410_UFSTAT      (0x18)
UART MODEM 状态寄存器(UMSTATn)UFSTAT0
UFSTAT1
0x5000001C
0x5000401C
#define S3C2410_UMSTAT      (0x1C)
UART 发送缓存寄存器(UTXHn)UTXH0

UTXH1

UTXH2
0x50000020(L)
0x50000023(B)
0x50004020(L)
0x50004023(B)
0x50008020(L)
0x50008023(B)
#define S3C2410_UTXH      (0x20)
UART 接收缓存寄存器(URXHn)URXH0

URXH1

URXH2
0x50000024(L)
0x50000027(B)
0x50004024(L)
0x50004027(B)
0x50008024(L)
0x50008027(B)
#define S3C2410_URXH      (0x24)
UART 波特率除数寄存器(UBRDIVn)UBRDIV0
UBRDIV1
UBRDIV2
0x50000028
0x50004028
0x50008028
#define S3C2410_UBRDIV      (0x28)

ULCON位描述在linux中的描述 linux-2.6.31/arch/arm/plat-s3c/include/plat/regs-serial.h
Reserved[7]  
Infrared Mode[6]决定是否使用红外模式
0 =正常模式操作
1 = 红外接收发送模式
#define S3C2410_LCON_IRM          (1<<6)
Parity Mode[5:3]在UART发送接收操作中定义奇偶码的生成和检
验类型
0xx = No parity
100 = Odd parity
101 = Even parity
110 = Parity forced/checked as 1
111 = Parity forced/checked as 0
#define S3C2410_LCON_PNONE      (0x0)
#define S3C2410_LCON_PEVEN      (0x5 << 3)
#define S3C2410_LCON_PODD      (0x4 << 3)
#define S3C2410_LCON_PMASK      (0x7 << 3)
Number of Stop Bit[2]定义度搜按个停止位用于帧末信号
0 =每帧一个停止位
1 =每帧两个停止位
#define S3C2410_LCON_STOPB      (1<<2)
Word Length[1:0]指出发送接收每帧的数据位数
00 = 5-bits 01 = 6-bits
10 = 7-bits 11 = 8-bits
#define S3C2410_LCON_CS5      (0x0)
#define S3C2410_LCON_CS6      (0x1)
#define S3C2410_LCON_CS7      (0x2)
#define S3C2410_LCON_CS8      (0x3)
#define S3C2410_LCON_CSMASK      (0x3)
 
UFCON位描述在linux中的描述

Tx FIFO Trigger Level
[7:6]决定发送FIFO的触发等级
00 = Empty 01 = 16-byte
10 = 32-byte 11 = 48-byte
#define S3C2440_UFCON_TXTRIG0      (0<<6)
#define S3C2440_UFCON_TXTRIG16      (1<<6)
#define S3C2440_UFCON_TXTRIG32      (2<<6)
#define S3C2440_UFCON_TXTRIG48      (3<<6)

Rx FIFO Trigger Level
[5:4]决定接收FIFO的触发等级
00 = 1-byte 01 = 8-byte
10 = 16-byte 11 = 32-byte
#define S3C2440_UFCON_RXTRIG1      (0<<4)
#define S3C2440_UFCON_RXTRIG8      (1<<4)
#define S3C2440_UFCON_RXTRIG16      (2<<4)
#define S3C2440_UFCON_RXTRIG32      (3<<4)
Reserved[3]  
Tx FIFO Reset[2]在重置FIFO后自动清除
0 = Normal
1= Tx FIFO reset
#define S3C2410_UFCON_RESETTX      (1<<2)
Rx FIFO Reset[1]在重置FIFO后自动清除
0 = Normal
1= Rx FIFO reset
#define S3C2410_UFCON_RESETRX      (1<<1)
#define S3C2410_UFCON_RESETBOTH      (3<<1)
FIFO Enable[0]0 = Disable
1 = Enable
 
默认设置为 
 #define S3C2410_UFCON_DEFAULT      (S3C2410_UFCON_FIFOMODE | \
                   S3C2410_UFCON_TXTRIG0  | \
                   S3C2410_UFCON_RXTRIG8 )

使能FIFO
Tx FIFO为空时触发中断
Rx FIFO中包含8个字节时触发中断
 
UFSTAT位描述在linux中的表示
Reserved[15]  
Tx FIFO Full[14]只要在发送操作中发送FIFO满,则自动置 1。
0 = 0-byte ≤ Tx FIFO data ≤ 63-byte
1 = Full
#define S3C2440_UFSTAT_TXFULL      (1<<14)
Tx FIFO Count[13:8]发送FIFO中的数据数量#define S3C2440_UFSTAT_TXSHIFT      (8)
Reserved[7]  
Rx FIFO Full[6]只要在接收操作中接收FIFO满,则自动置 1。
0 = 0-byte ≤ Rx FIFO data ≤ 63-byte
1 = Full
#define S3C2440_UFSTAT_RXFULL      (1<<6)
Rx FIFO Count[5:0]接收FIFO中的数据数量#define S3C2440_UFSTAT_RXSHIFT      (0)
    



  • uart驱动程序概述
        linux的uart驱动程序建立在tty驱动程序之上(串口也是一个终端),程序源代码主要在drivers/serial/目录下。其中 serial_core.c实现了uart设备的通用tty驱动层,其中定义了一组通用接口,包括3个结构体:uart_driver, uart_port,uart_ops( include/serial_core.h )。因此,实现一个平台的uart驱动程序只要实现这3个结构体即可。


serial_core(定义):samsumg.c (实现):                                                                   
struct uart_driver {
    struct module        *owner;
    const char        *driver_name;
    const char        *dev_name;
    int             major;
    int             minor;
    int             nr;
    struct console        *cons;

    /*
     * 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;
};
static struct uart_driver s3c24xx_uart_drv = {
    .owner        = THIS_MODULE,
    .dev_name    = "s3c2410_serial",//设备名称,这个名称必须和
        //根文件系统中/etc/inittab文件中的串口名称一致

    .nr        = CONFIG_SERIAL_SAMSUNG_UARTS,
    .cons        = S3C24XX_SERIAL_CONSOLE,
    .driver_name    = S3C24XX_SERIAL_NAME,//驱动名称
    .major        = S3C24XX_SERIAL_MAJOR,
    .minor        = S3C24XX_SERIAL_MINOR,
};
       
        uart_driver封装了tty_driver,使底层uart驱动不用关心ttr_driver。一个tty驱动程序必须注册/注销 tty_driver,而uart驱动则变为注册/注销uart_driver。使用如下接口:

int uart_register_driver(struct uart_driver *drv);
void uart_unregister_driver(struct uart_driver *drv);
实际上,uart_register_driver()和uart_unregister_driver()中分别包含了tty_register_driver()和tty_unregister_driver()的操作。

serial_core(定义):samsumg.h  samsumg.c(实现):
struct uart_port {
    spinlock_t        lock;            /* port lock */
    unsigned long        iobase;            /* in/out[bwl] */
    unsigned char __iomem    *membase;        /* read/write[bwl] */
    unsigned int        (*serial_in)(struct uart_port *, int);
    void            (*serial_out)(struct uart_port *, int, int);
    unsigned int        irq;            /* irq number */
    unsigned int        uartclk;        /* base uart clock */
    unsigned int        fifosize;        /* tx fifo size */
    unsigned char        x_char;            /* xon/xoff char */
    unsigned char        regshift;        /* reg offset shift */
    unsigned char        iotype;            /* io access style */
    unsigned char        unused1;

#define UPIO_PORT        (0)
#define UPIO_HUB6        (1)
#define UPIO_MEM        (2)
#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;    /* driver specific */
    unsigned int        ignore_status_mask;    /* driver specific */
    struct uart_info    *info;            /* pointer to parent info */
    struct uart_icount    icount;            /* statistics */

    struct console        *cons;            /* struct console, if any */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
    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_NO_TXEN_TEST    ((__force upf_t) (1 << 15))
#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))
/* The exact UART type is known and should not be probed.  */
#define UPF_FIXED_TYPE        ((__force upf_t) (1 << 27))
#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;            /* current modem ctrl settings */
    unsigned int        timeout;        /* character-based timeout */
    unsigned int        type;            /* port type */
    const struct uart_ops    *ops;
    unsigned int        custom_divisor;
    unsigned int        line;            /* port index */
    resource_size_t        mapbase;        /* for ioremap */
    struct device        *dev;            /* parent device */
    unsigned char        hub6;            /* this should be in the 8250 driver */
    unsigned char        suspended;
    unsigned char        unused[2];
    void            *private_data;        /* generic platform data pointer */
};
struct s3c24xx_uart_port {//封装了uart_port
    unsigned char            rx_claimed;
    unsigned char            tx_claimed;
    unsigned int            pm_level;
    unsigned long            baudclk_rate;

    unsigned int            rx_irq;
    unsigned int            tx_irq;

    struct s3c24xx_uart_info    *info;
    struct s3c24xx_uart_clksrc    *clksrc;
    struct clk            *clk;
    struct clk            *baudclk;
    struct uart_port        port;

#ifdef CONFIG_CPU_FREQ
    struct notifier_block        freq_transition;
#endif
};

static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = { //3 uarts default
    [0] = {
        .port = {
            .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
            .iotype        = UPIO_MEM,
            .irq        = IRQ_S3CUART_RX0,
            .uartclk    = 0,
            .fifosize    = 16,
            .ops        = &s3c24xx_serial_ops,
            .flags        = UPF_BOOT_AUTOCONF,
            .line        = 0,
        }
    },
    [1] = {
        .port = {
            .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
            .iotype        = UPIO_MEM,
            .irq        = IRQ_S3CUART_RX1,
            .uartclk    = 0,
            .fifosize    = 16,
            .ops        = &s3c24xx_serial_ops,
            .flags        = UPF_BOOT_AUTOCONF,
            .line        = 1,
        }
    },
#if CONFIG_SERIAL_SAMSUNG_UARTS > 2

    [2] = {
        .port = {
            .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
            .iotype        = UPIO_MEM,
            .irq        = IRQ_S3CUART_RX2,
            .uartclk    = 0,
            .fifosize    = 16,
            .ops        = &s3c24xx_serial_ops,
            .flags        = UPF_BOOT_AUTOCONF,
            .line        = 2,
        }
    },
#endif
#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
    [3] = {
        .port = {
            .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
            .iotype        = UPIO_MEM,
            .irq        = IRQ_S3CUART_RX3,
            .uartclk    = 0,
            .fifosize    = 16,
            .ops        = &s3c24xx_serial_ops,
            .flags        = UPF_BOOT_AUTOCONF,
            .line        = 3,
        }
    }
#endif
};
       
        uart_port用于描述一个UART端口(直接对应于一个串口)的I/O端口或I/O内存地址、FIFO大小、端口类型等信息。串口核心层提供如下函数来添加1个端口:

int uart_add_one_port(struct uart_driver *drv, struct uart_port *port);
        对上述函数的调用应该发生在uart_register_driver()之后,uart_add_one_port()的一个最重要作用是封装了 tty_register_device()。
        uart_add_one_port()的“反函数”是uart_remove_one_port(),其中会调用tty_unregister_device(),原型为:
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port);

serial_core.h(定义):samsumg.c (实现):                                                                                           
struct uart_ops {
    unsigned int    (*tx_empty)(struct uart_port *);
    void        (*set_mctrl)(struct uart_port *, unsigned int mctrl);
    unsigned int    (*get_mctrl)(struct uart_port *);
    void        (*stop_tx)(struct uart_port *);
    void        (*start_tx)(struct uart_port *);
    void        (*send_xchar)(struct uart_port *, char ch);
    void        (*stop_rx)(struct uart_port *);
    void        (*enable_ms)(struct uart_port *);
    void        (*break_ctl)(struct uart_port *, int ctl);
    int        (*startup)(struct uart_port *);
    void        (*shutdown)(struct uart_port *);
    void        (*flush_buffer)(struct uart_port *);
    void        (*set_termios)(struct uart_port *, struct ktermios *new,
                       struct ktermios *old);
    void        (*set_ldisc)(struct uart_port *);
    void        (*pm)(struct uart_port *, unsigned int state,
                  unsigned int oldstate);
    int        (*set_wake)(struct uart_port *, unsigned int state);

    /*
     * Return a string describing the type of the port
     */
    const char *(*type)(struct uart_port *);

    /*
     * Release IO and memory resources used by the port.
     * This includes iounmap if necessary.
     */
    void        (*release_port)(struct uart_port *);

    /*
     * Request IO and memory resources used by the port.
     * This includes iomapping the port if necessary.
     */
    int        (*request_port)(struct uart_port *);
    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);
#ifdef CONFIG_CONSOLE_POLL
    void    (*poll_put_char)(struct uart_port *, unsigned char);
    int        (*poll_get_char)(struct uart_port *);
#endif
};
static struct uart_ops s3c24xx_serial_ops = {
    .pm        = s3c24xx_serial_pm,
    .tx_empty    = s3c24xx_serial_tx_empty,
    .get_mctrl    = s3c24xx_serial_get_mctrl,
    .set_mctrl    = s3c24xx_serial_set_mctrl,
    .stop_tx    = s3c24xx_serial_stop_tx,
    .start_tx    = s3c24xx_serial_start_tx,
    .stop_rx    = s3c24xx_serial_stop_rx,
    .enable_ms    = s3c24xx_serial_enable_ms,
    .break_ctl    = s3c24xx_serial_break_ctl,
    .startup    = s3c24xx_serial_startup,
    .shutdown    = s3c24xx_serial_shutdown,
    .set_termios    = s3c24xx_serial_set_termios,
    .type        = s3c24xx_serial_type,
    .release_port    = s3c24xx_serial_release_port,
    .request_port    = s3c24xx_serial_request_port,
    .config_port    = s3c24xx_serial_config_port,
    .verify_port    = s3c24xx_serial_verify_port,
};
在使用串口核心层这个通用串口tty驱动层的接口后,一个串口驱动要完成的主要工作将包括:
  1. 定义uart_driver、uart_ops、uart_port等结构体的实例并在适当的地方根据具体硬件和驱动的情况初始化它们,当然具体设备 xxx的驱动可以将这些结构套在新定义的xxx_uart_driver、xxx_uart_ops、xxx_uart_port之内。
  2. 在模块初始化时调用uart_register_driver()和uart_add_one_port()以注册UART驱动并添加端口,在模块卸载时调用uart_unregister_driver()和uart_remove_one_port()以注销UART驱动并移除端口。
  3.  根据具体硬件的datasheet实现uart_ops中的成员函数,这些函数的实现成为UART驱动的主体工作。
  • 串口驱动初始化过程
        在S3C2410 串口驱动的模块加载函数中会调用uart_register_driver()注册s3c24xx_uart_drv这个uart_driver,同时经过
s3c2410_serial_init()→s3c24xx_serial_init()→platform_driver_register()的调用导致s3c24xx_serial_probe()被执行,而s3c24xx_serial_probe()函数中会调用 s3c24xx_serial_init_port()初始化UART端口并调用uart_add_one_port()添加端口。
platform_device_driver 参考:
Linux Platform Device and Driver    Linux driver model ----- platform

回过头来看s3c24xx_uart_info结构体(s3c24xx_uart_port的成员),是一些针对s3c2440 uart 的信息,在/drivers/serial/s3c2440.c中:

static struct s3c24xx_uart_info s3c2440_uart_inf = {
    .name        = "Samsung S3C2440 UART",
    .type        = PORT_S3C2440,
    .fifosize    = 64,
    .rx_fifomask    = S3C2440_UFSTAT_RXMASK,
    .rx_fifoshift    = S3C2440_UFSTAT_RXSHIFT,
    .rx_fifofull    = S3C2440_UFSTAT_RXFULL,
    .tx_fifofull    = S3C2440_UFSTAT_TXFULL,
    .tx_fifomask    = S3C2440_UFSTAT_TXMASK,
    .tx_fifoshift    = S3C2440_UFSTAT_TXSHIFT,
    .get_clksrc    = s3c2440_serial_getsource,
    .set_clksrc    = s3c2440_serial_setsource,
    .reset_port    = s3c2440_serial_resetport,
};


  • 串口操作函数,uart_ops接口函数
S3C2410串口驱动uart_ops结构体的startup ()成员函数s3c24xx_serial_startup()用于启动端口,申请端口的发送、接收中断,使能端口的发送和接收。

static int s3c24xx_serial_startup(struct uart_port *port)
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);
    int ret;

    dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
        port->mapbase, port->membase);

    rx_enabled(port) = 1;//接收使能

    ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
              s3c24xx_serial_portname(port), ourport);//申请接收中断,s3c24xx_serial_rx_chars是中断处理函数

    if (ret != 0) {
        printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
        return ret;
    }

    ourport->rx_claimed = 1;

    dbg("requesting tx irq...\n");

    tx_enabled(port) = 1;//发送使能

    ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
              s3c24xx_serial_portname(port), ourport);//申请发送中断

    if (ret) {
        printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
        goto err;
    }

    ourport->tx_claimed = 1;

    dbg("s3c24xx_serial_startup ok\n");

    /* the port reset code should have done the correct
     * register setup for the port controls */

    return ret;

 err:
    s3c24xx_serial_shutdown(port);
    return ret;
}

s3c24xx_serial_startup()的“反函数”为s3c24xx_serial_shutdown(),其释放中断,禁止发送和接收。

static void s3c24xx_serial_shutdown(struct uart_port *port)
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);

    if (ourport->tx_claimed) {
        free_irq(ourport->tx_irq, ourport);
        tx_enabled(port) = 0;
        ourport->tx_claimed = 0;
    }

    if (ourport->rx_claimed) {
        free_irq(ourport->rx_irq, ourport);
        ourport->rx_claimed = 0;
        rx_enabled(port) = 0;
    }
}

tx_empty()成员函数s3c24xx_serial_tx_empty()用于判断发送缓冲区是否为空,当使能FIFO模式的时候,判断UFSTATn寄存器,否则判断UTRSTATn寄存器的相应位。

static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
{
    struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
    unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);
    unsigned long ufcon = rd_regl(port, S3C2410_UFCON);

    if (ufcon & S3C2410_UFCON_FIFOMODE) {
        if ((ufstat & info->tx_fifomask) != 0 ||
            (ufstat & info->tx_fifofull))
            return 0;

        return 1;
    }

    return s3c24xx_serial_txempty_nofifo(port);
}

static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
{
    return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE);
}
在samsung.h中定义了如下操作寄存器的宏:
/* register access controls */

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

#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))  //read regester long

#define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg))
#define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg))

start_tx ()成员函数s3c24xx_serial_start_tx()用于启动发送,而stop_rx()成员函数 s3c24xx_serial_stop_tx()用于停止发送。

static void s3c24xx_serial_start_tx(struct uart_port *port)
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);

    if (!tx_enabled(port)) {
        if (port->flags & UPF_CONS_FLOW)
            s3c24xx_serial_rx_disable(port);

        enable_irq(ourport->tx_irq);
        tx_enabled(port) = 1;
    }
}
static void s3c24xx_serial_stop_tx(struct uart_port *port)
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);

    if (tx_enabled(port)) {
        disable_irq_nosync(ourport->tx_irq);
        tx_enabled(port) = 0;
        if (port->flags & UPF_CONS_FLOW)
            s3c24xx_serial_rx_enable(port);
    }
}

        S3C2410 串口驱动uart_ops结构体的set_termios()成员函数用于改变端口的参数设置,包括波特率、字长、停止位、奇偶校验等,它会根据传递给它的port、termios参数成员的值设置S3C2410 UART的ULCONn、UCONn、UMCONn等寄存器。

static void s3c24xx_serial_set_termios(struct uart_port *port,
                       struct ktermios *termios,
                       struct ktermios *old)
{
    struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
    struct s3c24xx_uart_port *ourport = to_ourport(port);
    struct s3c24xx_uart_clksrc *clksrc = NULL;
    struct clk *clk = NULL;
    unsigned long flags;
    unsigned int baud, quot;
    unsigned int ulcon;
    unsigned int umcon;
    unsigned int udivslot = 0;

    /*
     * We don't support modem control lines.
     */
    termios->c_cflag &= ~(HUPCL | CMSPAR);
    termios->c_cflag |= CLOCAL;

    /*
     * Ask the core to calculate the divisor for us.请求内核计算分频以便产生对应的波特率
     */

    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 = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);

    /* check to see if we need  to change clock source 检查以确定是否需要改变时钟源 */

    if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
        dbg("selecting clock %p\n", clk);
        s3c24xx_serial_setsource(port, clksrc);

        if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
            clk_disable(ourport->baudclk);
            ourport->baudclk  = NULL;
        }

        clk_enable(clk);

        ourport->clksrc = clksrc;
        ourport->baudclk = clk;
        ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
    }

    if (ourport->info->has_divslot) {
        unsigned int div = ourport->baudclk_rate / baud;

        udivslot = udivslot_table[div & 15];
        dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
    }
    /* 设置字长 */
    switch (termios->c_cflag & CSIZE) {
    case CS5:
        dbg("config: 5bits/char\n");
        ulcon = S3C2410_LCON_CS5;
        break;
    case CS6:
        dbg("config: 6bits/char\n");
        ulcon = S3C2410_LCON_CS6;
        break;
    case CS7:
        dbg("config: 7bits/char\n");
        ulcon = S3C2410_LCON_CS7;
        break;
    case CS8:
    default:
        dbg("config: 8bits/char\n");
        ulcon = S3C2410_LCON_CS8;
        break;
    }

    /* preserve original lcon IR settings */
    ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);

    if (termios->c_cflag & CSTOPB)
        ulcon |= S3C2410_LCON_STOPB;

    umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;

    if (termios->c_cflag & PARENB) {
        if (termios->c_cflag & PARODD)
            ulcon |= S3C2410_LCON_PODD;
        else
            ulcon |= S3C2410_LCON_PEVEN;
    } else {
        ulcon |= S3C2410_LCON_PNONE;
    }

    spin_lock_irqsave(&port->lock, flags);

    dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n",
        ulcon, quot, udivslot);

    wr_regl(port, S3C2410_ULCON, ulcon);
    wr_regl(port, S3C2410_UBRDIV, quot);
    wr_regl(port, S3C2410_UMCON, umcon);

    if (ourport->info->has_divslot)
        wr_regl(port, S3C2443_DIVSLOT, udivslot);

    dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
        rd_regl(port, S3C2410_ULCON),
        rd_regl(port, S3C2410_UCON),
        rd_regl(port, S3C2410_UFCON));

    /*
     * Update the per-port timeout.
     */
    uart_update_timeout(port, termios->c_cflag, baud);

    /*
     * Which character status flags are we interested in?
     */
    port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
    if (termios->c_iflag & INPCK)
        port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;

    /*
     * Which character status flags should we ignore?
     */
    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;

    /*
     * Ignore all characters if CREAD is not set.
     */
    if ((termios->c_cflag & CREAD) == 0)
        port->ignore_status_mask |= RXSTAT_DUMMY_READ;

    spin_unlock_irqrestore(&port->lock, flags);
}


  • 接收和发送中断处理函数
        在S3C2410 串口驱动中,与数据收发关系最密切的函数不是上述uart_ops成员函数,而是s3c24xx_serial_startup()为发送和接收中断注册的中断处理函数s3c24xx_serial_rx_chars()和s3c24xx_serial_tx_chars()。
        s3c24xx_serial_rx_chars ()读取URXHn寄存器以获得接收到的字符,并调用 uart_insert_char() 将该字符添加了tty设备的flip缓冲区中,当接收到64个字符或者不再能接受到字符后,调用tty_flip_buffer_push()函数向上层“推”tty设备的flip缓冲。
       s3c24xx_serial_tx_chars()读取uart_info中环形缓冲区中的字符,写入调用UTXHn寄存器。

#define S3C2410_UERSTAT_PARITY (0x1000)

static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id)
{
    struct s3c24xx_uart_port *ourport = dev_id;
    struct uart_port *port = &ourport->port;
    struct tty_struct *tty = port->info->port.tty;
    unsigned int ufcon, ch, flag, ufstat, uerstat;
    int max_count = 64;

    while (max_count-- > 0) {
        ufcon = rd_regl(port, S3C2410_UFCON);
        ufstat = rd_regl(port, S3C2410_UFSTAT);

        if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
            break;

        uerstat = rd_regl(port, S3C2410_UERSTAT);
        ch = rd_regb(port, S3C2410_URXH);

        if (port->flags & UPF_CONS_FLOW) {
            int txe = s3c24xx_serial_txempty_nofifo(port);

            if (rx_enabled(port)) {
                if (!txe) {
                    rx_enabled(port) = 0;
                    continue;
                }
            } else {
                if (txe) {
                    ufcon |= S3C2410_UFCON_RESETRX;
                    wr_regl(port, S3C2410_UFCON, ufcon);
                    rx_enabled(port) = 1;
                    goto out;
                }
                continue;
            }
        }

        /* insert the character into the buffer */

        flag = TTY_NORMAL;
        port->icount.rx++;

        if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
            dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
                ch, uerstat);

            /* check for break */
            if (uerstat & S3C2410_UERSTAT_BREAK) {
                dbg("break!\n");
                port->icount.brk++;
                if (uart_handle_break(port))
                    goto ignore_char;
            }

            if (uerstat & S3C2410_UERSTAT_FRAME)
                port->icount.frame++;
            if (uerstat & S3C2410_UERSTAT_OVERRUN)
                port->icount.overrun++;

            uerstat &= port->read_status_mask;

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

        if (uart_handle_sysrq_char(port, ch))
            goto ignore_char;

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

 ignore_char:
        continue;
    }
    tty_flip_buffer_push(tty);

 out:
    return IRQ_HANDLED;
}


static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
{
    struct s3c24xx_uart_port *ourport = id;
    struct uart_port *port = &ourport->port;
    struct circ_buf *xmit = &port->info->xmit;
    int count = 256;

    if (port->x_char) {
        wr_regb(port, S3C2410_UTXH, port->x_char);
        port->icount.tx++;
        port->x_char = 0;
        goto out;
    }

    /* if there isnt anything more to transmit, or the uart is now
     * stopped, disable the uart and exit
    */

    if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
        s3c24xx_serial_stop_tx(port);
        goto out;
    }

    /* try and drain the buffer... */

    while (!uart_circ_empty(xmit) && count-- > 0) {
        if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
            break;

        wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
        port->icount.tx++;
    }

    if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
        uart_write_wakeup(port);

    if (uart_circ_empty(xmit))
        s3c24xx_serial_stop_tx(port);

 out:
    return IRQ_HANDLED;
}


  • 串口测试
getty 功能说明:设置终端机模式,连线速率和管制线路,但是s3c2440_serial1无法工作,原因可能是因为开发板上只有一个串口。

serial-test





顶
0
踩
0
 
 
我的同类文章
LINUX串口驱动(8250)的编写与调试     串口控制器都是大同小异,用3条地址线就可以完全对串口控制器进行控制。针对ST554芯片来说,控制寄存器主要有THR(发送保持寄存器),RHR(接收保持寄存器),IER(中断使能寄存器),FCR(缓冲控制寄存器),LCR(控制寄存器),LSR(状态寄存器),MCR(模式控制寄存器),MSR(模式状态寄存器),DLL,DLM和测试寄存器等。    代码演示为linux-2.6.18。一般情况下,我们只要修改结构体uart_8250_port的赋值和中断处理函数。在uart_8250_port中的uart 阅读详情

相关推荐

RK3399之8250串口驱动

一、驱动整体框架二、驱动结构体对象三、驱动代码流程四、串口读写流程。

weixin_42703045的博客 3197

linux 设备驱动之8250串口驱动分析

linux 设备驱动之 8250 串口驱动    ------------------------------------------  本文系本站原创,欢迎转载! 转载请注明出处:http://ericxiao.cublog.cn/ -----------

bingfeng1210的专栏 2611

Linux串口驱动分析及移植

1 概述     在Linux中,常碰到“控制台”、“终端”、“console”、“tty”等术语,也会经常使用一些设备文件:/dev/console、/dev/ttyS0、/dev/ttyUSB0等。tty是Teletype的缩写,Teletype是最早出现的一种终端设备,Linux通常使用tty来表示“终端”,终端设备的种类有很多,比如串行终端、键盘和显示器、通过网络实现的终端等。     UART与USART都是单片机上的串口通信, UART(universal asynchronous receiv

weixin_40407893的博客 1万+

linux驱动基础系列--Linux 串口、usb转串口驱动分析

转载于:https://www.cnblogs.com/rongpmcu/p/7662278.html 前言   主要是想对Linux 串口、usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动、平台驱动等也不进行详细说明原理。如果有任何错误地方,请指出,谢谢! 声明:图和个别段落(我做了小的修改)是直接从网上截取 整体概述   ...

weixin_30802171的博客 174

Linux串口驱动程序(2)-串口驱动程序初始化分析

1.串口驱动程序结构分析 对用户来讲,能够正常使用串口肯定是需要实现如下函数的: 1、串口设备文件的打开 2、串口设备文件的初始化 3、串口设备文件的读写 4、串口设备文件的控制 2.串口驱动中重要的数据结构 首先分析一下串口读写的流程 当用户读写串口设备文件的时候,就会调用到usart_write函数(图中没有),在usart_write函数中会读取uart_state...

小虾米编程 1297

linux驱动之串口驱动分析

UART串口时序 UART使用异步模式工作,不需要时钟信号,其一般格式为:起始位+数据位+校验位+停止位。其中起始位1位,数据位5~8位,校验位0或1位,停止位1、1.5或2位。不过最常用的格式是1位起始位、8位数据位、没有奇偶校验、1位停止位,简记为8/N/1 8/N/1格式的时序图 TX、RX、GND信号外,UART中还会有诸如RTS、CTS等流控信号 空闲时数据线上规定为逻辑1。 开始传输数据时先发送起始位,规定为逻辑0,接收端会检测这个下降沿,以便之后开始采样接收数据。 起始位之后是数据位,规定先

xwb1040885790的博客 681

linux8250驱动支持5路串口,linux 设备驱动之8250串口驱动分析

linux 设备驱动之 8250 串口驱动------------------------------------------本文系本站原创,欢迎转载!转载请注明出处:http://ericxiao.cublog.cn/------------------------------------------一:前言前一段时间自己实践了一下8250芯片串口驱动的编写。今天就在此基础上分析一下linux ...

weixin_42365422的博客 787

嵌入式linux系统中串口驱动框架分析

串口是很常见的一个外设,在Linux下通常通过串口和其他设备或传感器进行通信。根据电平的不同,串口可以分为TTL和RS232。不管是什么样的电平接口,驱动程序是一样的。

嵌入式技术开发 461

linux+getty+串口_linux串口驱动分析

linux串口驱动分析硬件资源及描写叙述s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作。UART 使用系统时钟能够支持最高 115.2Kbps 的波特率。每一个 UART 通道对于接收器和发送器包含了 2 个 64 位的 FIFO。寄存器名称地址在linux中的描写叙述 (2410 和 2...

weixin_32802111的博客 707

linux系统中串口驱动框架基本分析(经典)

这些串行端口所对应的设备名称是/dev/tts/0(或/dev/ttyS0)、/dev/tts/1(或/dev /ttyS1)等,设备号分别是(4,0)、(4,1)等,分别对应于DOS系统下的COM1、COM2等。/dev/console 就是tty0 ,tty0则是当前所使用虚拟终端即激活的虚拟终端的一个别名,系统所产生的信息会发送到该终端上,实际上机器只有一个屏幕,也就是我们看到的这个屏幕,可以理解为console指向激活的那个tty,准确地说是激活的那个tty才将输出显示到console。

嵌入式技术开发 1084

西门子S7-1200与汇川SV660P伺服速度模式实战:从接线到编程全流程

本文详细解析了西门子S7-1200 PLC与汇川SV660P伺服驱动器在速度模式下的集成实战。内容涵盖从硬件接线、伺服参数关键设置(如电子齿轮比)、博图软件组态到PLC编程的全流程,并提供了故障排查与性能优化指南,帮助工程师快速实现精准、稳定的运动控制。

weixin_29248313的博客 487

Linux 串口驱动分析

(1)https://blog.csdn.net/qq_22847457/article/details/91477264 Linux串口驱动程序(1)-tty驱动程序架构 (2)https://blog.csdn.net/qq_22847457/article/details/91530701 Linux串口驱动程序(2)-串口驱动程序初始化分析 (3)https://blog.csdn.net...

weixin_40407893的博客 403

基于YOLOv5的隧道衬砌缺陷智能检测系统

计算机视觉在工程检测领域正发挥越来越重要的作用,特别是目标检测技术通过深度学习算法实现了对复杂场景的智能分析。YOLOv5作为当前主流的目标检测框架,通过改进网络结构和训练策略,可以显著提升检测精度和效率。在隧道工程领域,结合通道注意力机制和多尺度特征融合技术,能够有效解决衬砌缺陷识别中的小目标检测难题。这种AI检测系统相比传统人工判读地质雷达图像,不仅将效率提升20倍,还能将识别准确率提高到92.3%,特别适用于3cm以上结构缺陷的自动化检测。系统通过深度可分离卷积等优化,实现了模型轻量化,便于在边缘计算

weixin_29064103的博客 326

linux的串口驱动分析

原文地址::linux的串口驱动分析 - __小火车 - 博客园1、串口驱动中的数据结构 • UART驱动程序结构:struct uart_driver 驱动 • UART端口结构: struct uart_port 串口 • UART相关操作函数结构: struhttps://www.cnblogs.com/chd-zhangbo/p/5410336.html ...

xqhrs232的专栏 447

5张图,搞懂三方支付机构的“备付金账户”体系

本文将详细解析第三方支付机构各类备付金账户的功能定位、所承载的支付业务处理流程以及日常管理。

银行信息系统应用架构详解 2144

Ubuntu系统全盘备份——TimeShift的安装与使用

所以如果有那天你不小心作了个大死,并且幸运到还能打开GNOME桌面和Timeshift(只要不是执行sudo rm -f /*这样的自杀程序)的时候,点击你要恢复的那个备份文件再点击Restore,经过短暂的重启后你的系统就成功恢复了。打开软件后,会有一些初始化设置,只需要一直点击 Next。根据自己电脑的内存大小决定是自动备份还是手动备份。当然,对于虚拟机用户来说,没有比在客户机直接拷贝虚拟磁盘文件更棒的方式了。点击左上角的 Create,就会执行备份操作。最下面的意思就是说不会自动备份需要手动操作。

琉璃轻纱的博客 4703

Linux串口驱动分析初始化

代码来自:http://blog.csdn.net/longwang155069/article/details/42712551 * uart分析   *   * 其实串口分析就两个重要的文件: S3c2440.c  Samsung.c   *   * **/         /*1. 首先从Samsung.c的模块初始化函数看起*/      static int 

xiong6034的专栏 465

CPA-7-资产减值

摘要:本文系统梳理了资产减值会计处理规范,涵盖六大要点:1)不同资产适用的减值准则(存货、长期资产、金融资产);2)减值测试触发条件与例外情形(商誉等需年度强制测试);3)存货可变现净值计算方法及特殊考量;4)可收回金额的双重判定标准(公允价值净额与现值孰高);5)资产组认定标准及减值分摊顺序(先商誉后按比例);6)含少数股东商誉的减值测试方法(需调整包含完全商誉的账面价值)。文中通过具体案例演示了合并报表中商誉减值的计算过程,强调比较基准应包含少数股东权益对应的商誉价值。

qq_34047402的博客 203

Linux驱动-内核uart串口驱动分析

前言 写文章的目的是想通过记录自己的学习过程,以便以后使用到相关的知识点可以回顾和参考。 一、简介 串口是很常用的一个外设,在 Linux 下通常通过串口和其他设备或传感器进行通信,根据电平的不同,串口分为 TTL , RS232和RS485。不管是什么样的接口电平,其驱动程序都是一样的,通过外接 RS485 这样的芯片就可以将串口转换为 RS485 信号。 同 I2C、SPI 一样,Linux 也提供了串口驱动框架,我们只需要按照相应的串口框架编写驱动程序即可。串口驱动没有什么主机端和设备端之分,就只有一

qq_43286311的博客 4232

软考中级网络工程师通关指南:从学习到实战

本文是软考中级网络工程师备考实用指南,先介绍备考前准备,包括考试时间、形式、分值等基本信息,及引导读者评估自身基础以制定学习计划;再推荐官方教材、辅导书籍、在线课程平台和学习社区等优质资源。随后梳理网络协议、设备配置、安全、规划设计等重点知识,剖析企业网络故障排查与园区网络设计典型案例。还分享真题使用、解题技巧、错题本建立等习题解答方法,给出分阶段备考、时间安排与总结反思的备考计划,最后提供考场答题顺序、检查及心态调整策略,助力考生高效备考、顺利通关。

邓邓子的博客 1852

使用Transformer实现自动调制识别(RML2018)

image_size = 1024 # resize重置输入大小patch_size = 16 # 从输入图片中将被提取的块大小# 分成 num_patches 个块num_patches = (image_size // patch_size) #(72/6)**2 横着裁剪),],return x})

QAQIknow的博客 1323
上一篇: 视频下载
下一篇: Linux串口(serial、uart)驱动程序设计
rfidunion
博客等级 码龄10年 41粉丝 40原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值