samsung_serial驱动分析

尽管一个特定的UART设备驱动完全可以按照tty驱动的设计方法来设计,即定义tty_driver并实现tty_operations其中的成员函数,但是Linux已经在文件serial_core.c中实现了UART设备的通用tty驱动层,称为串口核心层,这样,UART驱动的主要任务变成了实现serial_core.c中定义的一组uart_xxx接口而非tty_xxx接口。

uart设备是继tty_driver的又一层封装.实际上uart_driver就是对应tty_driver.在它的操作函数中,将操作转入uart_port.在写操作的时候,先将数据放入一个叫做circ_buf的环形缓存区.然后uart_port从缓存区中取数据,将其写入到串口设备中.当uart_port从serial设备接收到数据时,会将设备放入对应line discipline的缓存区中.这样.用户在编写串口驱动的时候,只先要注册一个uart_driver.它的主要作用是定义设备节点号.然后将对设备的各项操作封装在uart_port.驱动工程师没必要关心上层的流程,只需按硬件规范将uart_port中的接口函数完成就可以了.

1.下图描述了串行系统间的层次结构关系,可以概括为:用户应用层 --> 线路规划层 --> TTY层 --> 底层驱动层 --> 物理硬件层

2.下图是串口核心层在整个tty源文件关系及数据流向中的位置:

  其中的xxx_uart.c在此处就是drivers/serial/samsung.c和s5pv210.c

3.接口关系:

从接口关系图可以看出,用户对uart设备操作的调用关系非常简单,

file_operations => [tty_ldisc_ops] => tty_operations => uart ops

其中tty_ldisc_ops线路规程并不是必要的,依赖于应用层设置是否使用ldisc处理数据。

4.UART驱动的总图:

5.uart驱动常用的数据结构表示如下:

6.Uart驱动程序主要围绕三个关键的数据结构展开(include/linux/serial_core.h中定义):

  UART特定的驱动程序结构定义:struct uart_driver s3c24xx_uart_drv;

  UART端口结构定义: struct s3c24xx_uart_port s3c24xx_serial_ports;

  UART相关操作函数结构定义: struct uart_ops s3c24xx_serial_ops;

【1】uart_driver 封装了tty_driver,使得底层的UART驱动无需关心tty_driver

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

其中的uart_state是设备私有信息结构体,

在uart_open()中:

tty->driver_data = state;

在其他uart_xxx()中:

struct uart_state *state = tty->driver_data;

就可以获取设备私有信息结构体。

static struct uart_driver s3c24xx_uart_drv= {
      .owner           =THIS_MODULE,
      .dev_name      = "s3c2440_serial",  //具体设备名称
      .nr          =CONFIG_SERIAL_SAMSUNG_UARTS,  //定义有几个端口
      .cons             = S3C24XX_SERIAL_CONSOLE,  //console接口
       .driver_name  =S3C24XX_SERIAL_NAME,  //串口名:ttySAC
      .major            =S3C24XX_SERIAL_MAJOR,  //主设备号
      .minor            =S3C24XX_SERIAL_MINOR,   //次设备号
};

一个tty驱动必须注册/注销tty_driver,而一个UART驱动则变为注册/注销uart_driver,使用如下接口:

int uart_register_driver(struct uart_driver *drv);

void uart_unregister_driver(struct uart_driver *drv);

【2】uart_port用于描述一个UART端口(直接对应于一个串口)的I/O端口或者IO内存地址等信息。

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 long        irqflags;        /* irq flags  */
    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_state    *state;            /* pointer to parent state */
    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;//UART操作集------->
    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 */
};

s3c24xx_uart_port 封装了uart_port:

struct s3c24xx_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
    int                channelnum;
};

static struct s3c24xx_uart_port
    s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
    [0] = {//串口0;
        .port = {
            .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
            .iotype        = UPIO_MEM,
            .irq        = IRQ_S3CUART_RX0,
            .uartclk    = 0,
            .fifosize    = 16,//定义FIFO缓存区大小 
            .ops        = &s3c24xx_serial_ops,//串口相关操作函数
            .flags        = UPF_BOOT_AUTOCONF,
            .line        = 0,//线路
        }
    },
    [1] = {//串口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
};

在xxx_probe()中:

struct s3c24xx_uart_port *ourport;//s3c24xx_uart_port封装了uart_port
ourport = &s3c24xx_serial_ports[dev->id];//s3c24xx_serial_ports是s3c24xx_uart_port结构体类型的

【3】uart_ops定义了针对UART的一系列操作,

/*
 * This structure describes all the operations that can be
 * done on the physical hardware.
 */
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);
    void        (*wake_peer)(struct uart_port *);

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

//一般来说,实现下面的成员函数是UART驱动的主体工作static struct uart_ops s3c24xx_serial_ops ={
      .pm         =s3c24xx_serial_pm,   //电源管理函数
      .tx_empty       = s3c24xx_serial_tx_empty, //检车发送FIFO缓冲区是否空
      .get_mctrl       = s3c24xx_serial_get_mctrl, //是否串口流控
      .set_mctrl       = s3c24xx_serial_set_mctrl, //是否设置串口流控cts
       .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,   //发送break信号
       .startup    =s3c24xx_serial_startup,   //串口发送/接收,以及中断申请初始配置函数
      .shutdown       = s3c24xx_serial_shutdown,  //关闭串口
      .set_termios    = s3c24xx_serial_set_termios,//串口clk,波特率,数据位等参数设置
      .type             = s3c24xx_serial_type,  // CPU类型关于串口
       .release_port   =s3c24xx_serial_release_port,  //释放串口
       .request_port   =s3c24xx_serial_request_port, //申请串口
      .config_port    = s3c24xx_serial_config_port,  //串口的一些配置信息info
      .verify_port    = s3c24xx_serial_verify_port,  //串口检测
      .wake_peer    = s3c24xx_serial_wake_peer,
};

而在serial_core.c中定义了tty_operations的实例,包含uart_open();uart_close();uart_send_xchar()等成员函数,这些函数借助uart_ops结构体中的成员函数来完成具体的操作。

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,
    .set_ldisc    = uart_set_ldisc,
    .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
    .proc_fops    = &uart_proc_fops,
#endif
    .tiocmget    = uart_tiocmget,
    .tiocmset    = uart_tiocmset,
#ifdef CONFIG_CONSOLE_POLL
    .poll_init    = uart_poll_init,
    .poll_get_char    = uart_poll_get_char,
    .poll_put_char    = uart_poll_put_char,
#endif
};

从下面的例子中可以看出串口核心层的tty_operations与uart_ops的关系:

/*
 * This function is used to send a high-priority XON/XOFF character to
 * the device
 */
static void uart_send_xchar(struct tty_struct *tty, char ch)
{
    struct uart_state *state = tty->driver_data;
    struct uart_port *port = state->uart_port;
    unsigned long flags;

    if (port->ops->send_xchar)/*如果uart_ops中实现了send_xchar成员函数*/
        port->ops->send_xchar(port, ch);
    else {
        port->x_char = ch;
        if (ch) {
            spin_lock_irqsave(&port->lock, flags);
            port->ops->start_tx(port);
            spin_unlock_irqrestore(&port->lock, flags);
        }
    }
}

这个例子的调用关系如下:

send_xchar  ---->  uart_send_xchar ---->  start_tx  --->  s3c24xx_serial_start_tx
时间: 2024-08-24 10:31:27

samsung_serial驱动分析的相关文章

20150311 NandFlash驱动分析

20150311 NandFlash驱动分析 2015-03-11 李海沿 一.结构体详解 MTD体系结构: 在linux中提供了MTD(Memory Technology Device,内存技术设备)系统来建立Flash针对linux的统一.抽象的接口 引入MTD后,linux系统中的Flash设备驱动及接口可分为4层: 设备节点 MTD设备层 MTD原始设备层 硬件驱动层 硬件驱动层:Flash硬件驱动层负责底层硬件设备实际的读.写.擦除,Linux MTD设备的NAND型Flash驱动位于

linux串口驱动分析

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

atheros wifi 驱动分析

Ar6003 驱动文档摘要 1.  wmi : wireless module interface //无线模块结构 2.  bmi : bootloader message interface 3.  htc : host target communications 4.  wps:wifi protected setup 5.  CS:connection services module 6.  STA:station 7.  AP:access point Wireless applica

蓝牙驱动分析 linux

蓝牙驱动分析 这个驱动分析的是OK6410开发板自带的内核版本是linux3.0.1,所支持的wifi和蓝牙一体芯片是marvell的8688和8787.根据开发板的设计,芯片与主机之间是通过sdio协议接口通信的,所以驱动也是通过sdio的方式写的. 个人分析驱动的过程是从插入设备驱动的动作开始的. 首先每次插入设备和拔出设备驱动都会通过终端打印相应的信息,判断在sd卡槽中肯定是触发中断的,通过看硬件原理图和数据手册中的SDMMC控制器可知用于mmc的中断号分别为56和57,回到代码中.在内核

[国嵌攻略][140][触摸屏驱动分析]

触摸屏驱动分析 初始化 1.使能ADC时钟 2.将物理地址转化为虚拟地址 3.让触摸屏进入等待中断模式 4.分配输入设备结构 5.设置可能上报的事件类型和按键类型 6.为TC和ADC中断注册处理函数 7.注册输入型设备 按下处理 1.判断按下或弹起 2.如果是按下情况,那么启动XY坐标的AD转换 3.进行4次ADC转换,获取4次XY坐标值 4.计算4次采集的平均值,并上报给内核

【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析

[linux驱动分析]之dm9000驱动分析(一):dm9000原理及硬件分析 [linux驱动分析]之dm9000驱动分析(二):定义在板文件里的资源和设备以及几个宏 [linux驱动分析]之dm9000驱动分析(四):net_device结构体 [linux驱动分析]之dm9000驱动分析(五):另外几个重要的结构体 [linux驱动分析]之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现 [linux驱动分析]之dm9000驱动分析(七):dm9000的卸

linux input设备驱动分析

linux input设备驱动分析 工作机制 输入设备工作机制: 输入动作–>产生中断–>CPU通过总线或者IO读取数据到缓冲区 构架层次 app //-------------------- input_event_driver //-------------------- input_core //-------------------- input_device_driver //-------------------- hardware 数据结构 输入设备对象 struct input

S3C6410 LCD驱动分析(转)

一. 理论分析1. 几个概念:FIMC :    Fully Interactive Mobile Camera (完全交互式移动摄像机)FIMD:     Fully Interactive Mobile Display (完全交互式移动显示设备)2. 设置VCLK在VIDCON0中bit[3:2]-->Select the Video Clock source =00 --> HCLK=133MHZbit[13:6] --> CLKVAL_F = 13  (这个值是在驱动中计算出来的

RK30SDK开发板驱动分析(二):DDR频率配置

在内核配置界界面,我们可以很容易的配置DDR的频率,300M OR 600M, so easy! 那么它是如何起作用的呢? 回想 RK30SDK开发板驱动分析(一) 末尾提到MACHINE_START是系统开始的地方,位于board-rk30-box.c ,注意里面有个函数rk30_map_io MACHINE_START(RK30, "RK30board") .boot_params = PLAT_PHYS_OFFSET + 0x800, .fixup = rk30_fixup, .