当前位置: 首页 > 编程日记 > 正文

linux串口驱动分析

linux串口驱动分析



  • 硬件资源及描写叙述
        s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 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用于描写叙述一个UARTport(直接相应于一个串口)的I/Oport或I/O内存地址、FIFO大小、port类型等信息。串口核心层提供例如以下函数来加入�1个port:

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()用于启动port,申请port的发送、接收中断,使能port的发送和接收。

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





相关文章:

计算TD-LTE DL 峰值速率的工具和相关参数

前一段时间测试DT 基站&#xff0c;需要配置TDD LTE cell 的UDC、TM模式来验证不同组合下的下行峰值速率&#xff0c;趁此机会我用excel写了一个计算下行峰值速率的工具。工具上传至我的github: https://github.com/greenricky/tdlte_dl_rate 计算峰值速率的常用办法是参考36.2…

java配置中心开源项目_配置中心搭建(spring-cloud-config-server)

1.github创建配置库2.配置服务端①创建项目②导入jarorg.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-testorg.springframework.cloudspring-cloud-starter-netflix-eureka-clientorg.springframework.cloudspring-cloud-config-…

detection in video and image

video中的detection&#xff0c;背景更加复杂&#xff0c;目标更加不聚焦&#xff0c;同时由于图片分辨率低于图像&#xff0c;因此更加难做。 image中的Detection,背景相对简单些&#xff0c;目标更加聚焦&#xff0c;同时图片分辨率高&#xff0c;因此更加容易些。 转载于:ht…

烂泥:U盘安装Centos6.5

本文首发于烂泥行天下。使用U盘安装Centos6.5&#xff0c;需要以下几个步骤&#xff1a;1、 制作U盘linux系统2、 设置服务器BIOS3、 安装Centos&#xff0c;注意引导分区的安装首先要把U盘做成linux启动盘。网上有关这方面的软件比较多&#xff0c;在此我使用的是WinSetupFrom…

http status 汇总

http status 汇总 常见HTTP状态码 200 OK301 Moved Permanently302 Found304 Not Modified307 Temporary Redirect400 Bad Request401 Unauthorized403 Forbidden404 Not Found410 Gone500 Internal Server Error501 Not Implemented100 Continue 初始的请求已经接受&#xf…

hdu 4278 2012天津赛区网络赛 数学 *

8进制转为10进制 1 #include<cstdio>2 #include<iostream>3 #include<algorithm>4 #include<cstring>5 #include<cmath>6 #include<queue>7 #include<map>8 using namespace std;9 #define MOD 1000000007 10 const int INF0x3f3f3f…

slub object 内存布局

我在 https://blog.csdn.net/wowricky/article/details/83218126 介绍了一种内存池&#xff0c;它的实现类似于linux 中打开slub_debug (1. make menuconfig: Kenel hacking -> Memory Debugging, 2. comand line中传入slub_debugPZU) 时slub 对象池。 首先我们先看一下slub…

java wait abc_java----wait/notify

解释wait/notify必须配合synchronized使用democlass NotifyStop2 {private final Object lock new Object();public void add(String s) throws InterruptedException {Thread.sleep(1000);synchronized (lock) {System.out.println("add notify qian");//唤醒其他线…

hdu-1166敌兵布阵

这个题目就是考察线段树的基本用法&#xff0c;我自己打了代码&#xff0c;其实就是照模板来的&#xff0c;大概思想已经弄懂了。用c不能过&#xff0c;说我超时&#xff0c;就改成c的读入读出&#xff0c;这坑爹的过了。我最爱的c&#xff0c;你肿么了。。。 这是ac的代码&…

Android数据存储(三)——SQLite

如果需要一个更加健壮的数据存储机制&#xff0c;则需要使用一个关系型数据库&#xff0c;在Android上&#xff0c;则为SQLlite。 SQLite的特点&#xff1a;轻量级、嵌入式的、关系型数据库。可移植性好&#xff0c;易使用&#xff0c;小&#xff0c;高效且可靠&#xff0c;与使…

3GPP组织和协议概述

3GPP组织概述 1. TSG/WG 3GPP是以工作组开展工作的&#xff0c;目前有3个大的技术规范组&#xff1a;RAN, SA, CT, 这一级别的工作组英语写为 TSG (Technical Specification Group)。每个TSG下面又分了很多工作组(WG: work group). 详见下表&#xff1a; https://www.3gpp.or…

mysql被拖垮_说几个拖垮系统的小细节!

本文首发于个人微信公众号《andyqian》,期待你的关注&#xff01;前言有好几天没有更新了&#xff0c;期间确实比较忙些&#xff0c;以至于周末也没休息。在这期间&#xff0c;感触还是蛮深的。现在碎片化的想法等整理好后&#xff0c;再以文章的形式分享出来。今天要说的是另外…

为 区域添加 Tag

tagsinput selectTag 转载于:https://www.cnblogs.com/yunqianduan/p/4740902.html

android中文字中间有超链接的实现方法

1.XML里写&#xff1a; <resources> <string name"ACCOUNT_REGISTER_PROMPT_AGREEMENT">点击注册&#xff0c;表示同意<a href"http://www.ouragreement.com">服务协议</a></string> </resources> <TextView …

4.4 类型转换

from http://www.learncpp.com/cpp-tutorial/44-type-conversion-and-casting/ 类型隐式转换 类型隐式转换的步骤如下所示&#xff1a; Long double (highest)DoubleFloatUnsigned long intLong intUnsigned intInt (lowest) 最底层是int而不是short或是char&#xff0c;是因为…

LFSR 和 m序列

翻译自&#xff1a;sharetechnote: LFSR LFSR Linear Feedback Shift Register - 线性反馈移位寄存器 LFSR 是一种移位寄存器电路&#xff0c;其中两个或多个中间步骤的输出线性组合并反馈到输入值&#xff0c;这就是为什么它被称为线性反馈移位寄存器的原因。 该电路具有以…

python合并k个有序链表_Leetcode合并K个升序链表(Python版本),LeetCode,python

一、描述给你一个链表数组&#xff0c;每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。示例 1&#xff1a;输入&#xff1a;lists [[1,4,5],[1,3,4],[2,6]]输出&#xff1a;[1,1,2,3,4,4,5,6]解释&#xff1a;链表数组如下&…

Qt界面风格设置

每个widget都可以设置风格setStyle(QStyle style)对QApplication设置QStyle即对所有QApplication::setStyle(QStyleFactory::create("Fusion"));其他widget如过没有被设置QStyle&#xff0c;默认使用QApplication的QStyle主要可重写接口绘制复杂控件virtual void …

树莓派练习程序(蜂鸣器)

蜂鸣器模块如下图&#xff1a; 树莓派的引脚如下图&#xff1a; 我们将Vcc引脚连接物理接口1&#xff08;注意这里需要用3.3v&#xff09;&#xff0c;I/O引脚连接物理接口40&#xff0c;GND引脚连接物理接口39。 实物连接如下图&#xff1a; 编程使用WiringPi库&#xff0c;使…

Gold Code,Gold Sequence

Gold Code Gold Code是以Robert Gold的名字命名的。它是一组特殊的二进制随机(伪随机)序列&#xff0c;其中成员序列之间的相关性很小。由于这种特性(较小的相关性)&#xff0c;它被广泛地用作各种无线通信系统的扰码。 我们可以非常简单地利用 m序列 来生成Gold Code: 选择两…

【PHP高效搜索专题(1)】sphinxCoreseek的介绍与安装

我们已经知道mysql中带有"%keyword%"条件的sql是不走索引的,而不走索引的sql在大数据量大并发量的时候,不仅效率极慢还很有可能让数据库崩溃.那我们如何通过某些关键字来搜索我们想要的文章呢? 虽然mysql的MYISAM提供全文索引,但是只支持中文,并且性能却不敢让人恭维…

java 开源sns_JEESNS V1.0发布,JAVA 开源 SNS 社交系统

JEESNS V1.0 发布了&#xff0c;本次更新内容&#xff1a;增加后台管理员授权与取消功能增加私信模块解决在微博页面&#xff0c;左侧微博点赞过后&#xff0c;左侧展示列表小手会变黑&#xff0c;但是右侧热门出小手依然是白色修复后台添加栏目、文章成功后&#xff0c;提示页…

Balanced Binary Tree leetcode java

题目&#xff1a;Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1. 题解&#xff1a;采用递归…

leaflet地图框架

leaflet 中文API LeafLet js 官网&#xff1a; http://leafletjs.com/index.html LeafLet js 官网demo&#xff1a; http://leafletjs.com/examples.html LeafLet js 官网API&#xff1a; http://leafletjs.com/reference-1.3.0.html L.Map API各种类中的核心部分&#xff0c;用…

NR:UE初始搜网流程

UE的初始搜网流程&#xff0c;PSS->SSS->PBCH->RMSI.我画了一个简单的流程图如下&#xff0c;里面标注了每个环节的重点。 UE的初始搜网流程: 分为SSB同步(包括MIB读取)和RMSI的读取。 1. SSB SSB包括&#xff1a; PSS,SSS,PBCH. UE 在GSCN频点上&#xff0c;搜索…

纯CSS3制作的圆角效果按钮菜单

<!DOCTYPE html> <head> <meta http-equiv"Content-Type" content"text/html; charsetutf-8" /> <title>纯CSS3制作的圆角效果按钮菜单丨曲阳雕塑</title> <style type"text/css"> nav{display: block; wid…

java 左右键_js 区分鼠标左右键点击

oncontextmenu 是右键事件但是滚轮事件并没有获取到, 使用vue可以用middle获取Title.box {width: 200px;height: 200px;background: deepskyblue;}let div document.getElementById(app)div.oncontextmenu function (e) {e.preventDefault();console.log(右键, e.button)};d…

面向对象设计领域的OCP原则

一、OCP简介&#xff08;OCP--Open-Closed Principle &#xff09;&#xff1a;Software entities(classes,modules,functions,etc.) should be open for extension, but closed for modification。软件实体应当对扩展开放&#xff0c;对修改关闭&#xff0c;即软件实体应当在不…

Python教学课程分享9-面向对象编程

面向对象程序设计的思想主要是针对大型软件设计而提出的&#xff0c;它的一个关键性观念是将数据以及对数据的操作封装在一起&#xff0c;组成一个相互依存、不可分割的整体&#xff0c;即不同对象之间通过消息机制来通信或者同步。对于相同类型对象进行分类、抽象后&#xff0…

UE capability与 双连接相关的参数。

UE capability 分为 Network capability 和 Radio capability, 即网络能力和无线能力。 Netowrk Capability UE 在做Attach Request 时会主动上报自己的网络能力&#xff1b;Radio Capability 网络侧下发Enquiry Capability来请求UE无线能力&#xff0c;UE 回复capability inf…