ARM-Linux S5PV210 UART驅動(3)----串口核心層、關鍵結構體、接口關系


      盡管一個特定的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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM