1. 程式人生 > >AM335X 串列埠驅動學習(1)-基於linux3.8核心

AM335X 串列埠驅動學習(1)-基於linux3.8核心

學習串列埠驅動,先從資料結構入手吧。串列埠驅動有3個核心資料結構:

(/drivers/tty/serial/omap-serial.c)
- UART特定的驅動程式結構定義:struct uart_driver serial_omap_reg;
- UART埠結構定義: struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];
- UART相關操作函式結構定義: struct uart_ops serial_omap_pops;

重要資料結構

1. uart_driver

uart_driver 封裝了tty_driver,使得底層的UART驅動無需關心tty_driver。

#include <linux/serial_core.h>

  struct uart_driver {
      struct module    *owner;      /* 擁有該uart_driver的模組,一般為       THIS_MODULE */
      const char    *driver_name; /* 串列埠驅動名,串列埠裝置檔名以驅動名為基礎 */
      const char    *dev_name;      /* 串列埠裝置名 */
      int             major;          /* 主裝置號 */
      int
minor; /* 次裝置號 */ int nr; /* 該uart_driver支援的串列埠個數(最大) */ struct console *cons; /* 其對應的console.若該uart_driver支援serial console,否則為NULL */ /* * these are private; the low level driver should not * touch these; they should be initialised to NULL */
struct uart_state *state; struct tty_driver *tty_driver; };

其中的uart_state是裝置私有資訊結構體,

在uart_open()中:

tty->driver_data = state;

在其他uart_xxx()中:

struct uart_state *state = tty->driver_data;

就可以獲取裝置私有資訊結構體。

#include<linux/serial_core.h>

static struct uart_driver serial_omap_reg = {
    .owner      = THIS_MODULE,
    .driver_name    = "OMAP-SERIAL",
    .dev_name   = OMAP_SERIAL_NAME,
    .nr     = OMAP_MAX_HSUART_PORTS,
    .cons       = OMAP_CONSOLE,
};

一個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記憶體地址等資訊;實際上,一個uart_port例項對應一個串列埠裝置。

#include<linux/serial_core.h>

  struct uart_port {
      spinlock_t        lock;            /* port lock 串列埠埠鎖 */
      unsigned long        iobase;        /* in/out[bwl] io埠基地 */
      unsigned char __iomem    *membase;/* read/write[bwl] IO記憶體基地址,經對映(如ioremap)後的IO記憶體虛擬基地址 */
      unsigned int        (*serial_in)(struct uart_port *, int); 
      void            (*serial_out)(struct uart_port *, int, int);
      void            (*set_termios)(struct uart_port *,
                                 struct ktermios *new,
                                 struct ktermios *old);
      void            (*pm)(struct uart_port *, unsigned int state,
                        unsigned int old);
      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 */
  #define UPIO_DWAPB32        (8)            /* DesignWare APB UART (32 bit accesses) */

      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        irq_wake;
      unsigned char        unused[2];
      void            *private_data;        /* generic platform data pointer */
  };

uart_omap_port 封裝了uart_port:

#inclide<linux/serial_core.h>

struct uart_omap_port {
    struct uart_port    port;
    struct uart_omap_dma    uart_dma;
    struct device       *dev;

    unsigned char       ier;
    unsigned char       lcr;
    unsigned char       mcr;
    unsigned char       fcr;
    unsigned char       efr;
    unsigned char       dll;
    unsigned char       dlh;
    unsigned char       mdr1;
    unsigned char       scr;

    int         use_dma;
    /*
     * Some bits in registers are cleared on a read, so they must
     * be saved whenever the register is read but the bits will not
     * be immediately processed.
     */
    unsigned int        lsr_break_flag;
    unsigned char       msr_saved_flags;
    char            name[20];
    unsigned long       port_activity;
    int         context_loss_cnt;
    u32         errata;
    u8          wakeups_enabled;

    int         DTR_gpio;
    int         DTR_inverted;
    int         DTR_active;

    struct pm_qos_request   pm_qos_request;
    u32         latency;
    u32         calc_latency;
    struct work_struct  qos_work;
    struct pinctrl      *pins;
    struct serial_rs485 rs485;
};
static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];

由此可見,對串列埠的一系列初始化,實際上落到了對ui結構體的填充上。

在serial_omap_probe(struct platform_device *pdev)中:
struct uart_omap_port up; /*uart_omap_port 封裝了uart_port /
ui[up->port.line] = up;

serial_omap_add_console_port(up);
uart_add_one_port(&serial_omap_reg, &up->port);

  • uart_iconut為串列埠資訊計數器,包含了傳送字元計數、接收字元計數等。在串列埠的傳送中斷處理函式和接收中斷處理函式中,我們需要管理這些計數;
   struct uart_icount {
      __u32    cts;
      __u32    dsr;
      __u32    rng;
      __u32    dcd;
      __u32    rx;      /* 傳送字元計數 */
      __u32    tx;      /* 接受字元計數 */
      __u32    frame;   /* 幀錯誤計數 */
      __u32    overrun; /* Rx FIFO溢位計數 */
      __u32    parity;  /* 幀校驗錯誤計數 */
      __u32    brk;     /* break計數 */
      __u32    buf_overrun;
   };
  • uart_stat有兩個成員在底層串列埠驅動會用到:xmit和port。使用者空間程式通過串列埠傳送資料時,上層驅動將使用者資料儲存在xmit;而串列埠傳送中斷處理函式就是通過xmit獲取到使用者資料並將它們傳送出去。串列埠接收中斷處理函式需要通過port將接收到的資料傳遞給行規則層。
  /*
   * This is the state information which is persistent across opens.
   */
  struct uart_state {
      struct tty_port        port;

      int            pm_state;
      struct circ_buf        xmit;

      struct tasklet_struct    tlet;
      struct uart_port    *uart_port;
 };

3. uart_ops

uart_ops涵蓋了串列埠驅動可對串列埠裝置進行的所有操作

  /*
   * This structure describes all the operations that can be
   * done on the physical hardware.
   */
  struct uart_ops {
      unsigned int    (*tx_empty)(struct uart_port *);/* 串列埠的Tx FIFO快取是否為空 */
      void        (*set_mctrl)(struct uart_port *, unsigned int mctrl);/* 設定串列埠modem控制 */
      unsigned int    (*get_mctrl)(struct uart_port *); /* 獲取串列埠modem控制 */
      void        (*stop_tx)(struct uart_port *);/* 禁止串列埠傳送資料 */
      void        (*start_tx)(struct uart_port *);/* 使能串列埠傳送資料 */
      void        (*send_xchar)(struct uart_port *, char ch);/* 傳送xChar */
      void        (*stop_rx)(struct uart_port *);/* 禁止串列埠接收資料 */
      void        (*enable_ms)(struct uart_port *);/* 使能modem的狀態訊號 */
      void        (*break_ctl)(struct uart_port *, int ctl); /* 設定break訊號 */
      int        (*startup)(struct uart_port *);/* 啟動串列埠,應用程式開啟串列埠裝置檔案時,該函式會被呼叫 */
      void        (*shutdown)(struct uart_port *);/* 關閉串列埠,應用程式關閉串列埠裝置檔案時,該函式會被呼叫 */
      void        (*flush_buffer)(struct uart_port *);
      void        (*set_termios)(struct uart_port *, struct ktermios *new,
                         struct ktermios *old);/* 設定串列埠引數 */
      void        (*set_ldisc)(struct uart_port *, int new);
      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 *);/* 釋放串列埠已申請的IO埠/IO記憶體資源,必要時還需iounmap */

      /*
       * Request IO and memory resources used by the port.
       * This includes iomapping the port if necessary.
       */
      int        (*request_port)(struct uart_port *); /* 申請必要的IO埠/IO記憶體資源,必要時還可以重新對映串列埠埠 */
      void        (*config_port)(struct uart_port *, int);/* 執行串列埠所需的自動配置 */
      int        (*verify_port)(struct uart_port *, struct serial_struct *);/* 驗證串列埠所需的自動配置 */
      int        (*ioctl)(struct uart_port *, unsigned int, unsigned long);
  #ifdef CONFIG_CONSOLE_POLL
      void    (*poll_put_char)(struct uart_port *, unsigned char);
      int        (*poll_get_char)(struct uart_port *);
  #endif
  };

UART驅動的主體工作通過下列函式來實現:

#include<>
static struct uart_ops serial_omap_pops = {
    .tx_empty   = serial_omap_tx_empty,/*檢車傳送FIFO緩衝區是否空*/
    .set_mctrl  = serial_omap_set_mctrl,/*是否設定串列埠流控cts*/
    .get_mctrl  = serial_omap_get_mctrl,/*是否串列埠流控*/
    .stop_tx    = serial_omap_stop_tx,/*停止傳送*/
    .start_tx   = serial_omap_start_tx,/*啟動傳送*/
    .throttle   = serial_omap_throttle,/**/
    .unthrottle = serial_omap_unthrottle,/**/
    .stop_rx    = serial_omap_stop_rx,/*停止接收*/
    .enable_ms  = serial_omap_enable_ms,/**/
    .break_ctl  = serial_omap_break_ctl,/*傳送break訊號*/
    .startup    = serial_omap_startup,/*串列埠傳送/接收,以及中斷申請初始配置函式*/
    .shutdown   = serial_omap_shutdown,/*關閉串列埠*/
    .set_termios    = serial_omap_set_termios,/*串列埠clk,波特率,資料位等引數設定*/
    .pm     = serial_omap_pm,/*電源管理函式*/
    .set_wake   = serial_omap_set_wake,/**/
    .type       = serial_omap_type,/*CPU型別關於串列埠*/
    .release_port   = serial_omap_release_port,/*釋放串列埠*/
    .request_port   = serial_omap_request_port,/*申請串列埠*/
    .config_port    = serial_omap_config_port,/*串列埠的一些配置資訊info*/
    .verify_port    = serial_omap_verify_port,/*串列埠檢測*/
    .ioctl      = serial_omap_ioctl,/**/
#ifdef CONFIG_CONSOLE_POLL
    .poll_put_char  = serial_omap_poll_put_char,/**/
    .poll_get_char  = serial_omap_poll_get_char,/**/
#endif
};

而在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);
        }
    }
}

(待續)

相關推薦

AM335X 串列驅動學習1-基於linux3.8核心

學習串列埠驅動,先從資料結構入手吧。串列埠驅動有3個核心資料結構: (/drivers/tty/serial/omap-serial.c) - UART特定的驅動程式結構定義:struct uart_driver serial_omap_reg; - U

busybox檔案系統與簡單驅動學習1-busybox檔案系統搭建與nfs掛載配置

一、busybox編譯安裝 2、參考資料:訊為4412精英版開發手冊9.5章節 3、實際操作環境:Ubuntu14.04、交叉編譯環境 arm-none-linux-gnueabi-(直接用訊為提供的,也可以網上用其他的)、busybox版本

Air202學習1認識Air202 S6 核心

1、外設分佈圖  上圖可以看出,當我們想讓模組開機自啟動時,只需把【上電自動開機焊點】用焊錫點上即可。 2、排針管腳 模組可以採用4V/5V直流供電,可以用USB-TTL供電。 HOST_TX,HOST_RX為下載、除錯串列埠。連線方式如下: 3、關於板載L

串列驅動程式設計詳解---串列初始化

TTY驅動程式架構: 1. TTY概念解析     1.1 /dev/ttySCA0     1.2 /dev/tty1-n     1.3 /dev/console       在linux系統中,終端是一類字元型裝置,它包括多種型別,通常使用tty來簡稱各種型別的終端裝

Linux 網卡驅動學習網絡驅動接口小結

-a key 頻率 網絡 上網 ren 網絡設備 ews 入口 【摘要】前文我們分析了一個虛擬硬件的網絡驅動樣例。從中我們看到了網絡設備的一些接口。事實上網絡設備驅動和塊設備驅動的功能比較相似,都是發送和接收數據包(數據請求)。當然它們實際是有非常多不同

redis學習1--- NoSQL介紹

redis學習 模型 width 快速查詢 init 文件 borde 處理 only 一、NoSQL介紹 1、什麽是NoSQL NoSQL = Not Only SQL 非關系型數據庫 2、為什麽用NoSQL High performance - 高

Linux 網卡驅動學習應用層、tcp 層、ip 層、設備層和驅動層作用解析

local acc 每次 letter auto sizeof style article inode 本文將介紹網絡連接建立的過程、收發包流程,以及當中應用層、tcp層、ip層、設備層和驅動層各層發揮的作用。 1、應用層 對於使用socket進行網絡連接的serv

ArcGIS API for JavaScript學習1:第一個地圖

樣式表 參數 資源 charset 底層 arcgis 順序 api navi 1.簡介 ArcGIS API for JavaScript跟隨ArcGIS 9.3同時發布,是ESRI根據JavaScript技術實現的調用ArcGIS Server REST API接口的一

Linux學習1

linux開始學習Linux了,希望能學有所成————測試中本文出自 “Linux學習” 博客,請務必保留此出處http://mmchy.blog.51cto.com/13044974/1941714Linux學習(1)

facets學習1:什麽是facets

遺失 air 不同的 無縫切換 over 快速 後來 樣式 觀察 ML 數據集可以包含數億個數據點,每個數據點由數百(甚至數千)的特征組成,幾乎不可能以直觀的方式了解整個數據集。為幫助理解、分析和調試 ML 數據集,谷歌開源了 Facets,一款可視化工具。 Facets

C++學習1:最大子段和多種解法

多少 問題: code namespace 數據 組成 amp using () 問題:給定由n個數(可能為負數)組成的序列a1,a2,a3,...,an,求該序列子段和的最大值。 第一種解法:(最容易考慮的方法,將所有的子段一一相加,然後比較) 1 #include&

嵌入式開發學習1<ARM體系結構>

特點 如何 現在 訪問 任務 物聯網 嵌入 專用 arm1 SoC : 在cpu 裏內嵌了很多外設,現在所說的cpu 實際上都是SoC。 32位cpu指的是數據總線是32位的。 32位的地址總線尋址範圍是4G。2的32次方。 CISC complex instruction

IDEA 學習筆記之 Java項目開發深入學習1

java項目 bsp 重構 str 代碼提示 log pan ora tro Java項目開發深入學習(1): 定義編譯輸出路徑: 繼承以上工程配置 重新定義新的項目編譯路徑 添加source目錄:點擊添加,再點擊移除: 編譯項目: 常用快捷鍵總結: Ctr

nodejs學習1

管理工具 cti 分享 環境 scrip alt world 命令提示符 true 1.安裝:可以在http://nodejs.org/dist/下載nodejs的所有版本,我在裏面下載了node-v8.9.0-x64.msi,安裝完成,在命令提示符中輸入“node --v

Win10下Docker學習1安裝

ner table 組成 高效率 down 提高 打開 cpu 其他人 Docker簡介 Docker 是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,然後發布到任何流行的 Linux 機器上,也可以實現虛擬化。容器是完全使用沙箱機制,

bartender鏈接數據庫提示:無法鏈接到數據庫 外部數據驅動程序1中的意外錯誤#6670

bartender 6670 外部數據驅動程序bartender鏈接數據庫提示:無法鏈接到數據庫 外部數據驅動程序(1)中的意外錯誤#6670是 微軟 10月份更新的補丁導致。解決:1.關閉自動更新2.卸載補丁:win7 KB4041678 KB404168

Vue深度學習1

msg 第一個 數據綁定 vue.js 這就是 one round 本質 dex Hello World 現在就讓我們來寫第一個vue.js的實例。如下代碼: html代碼: <div id="demo"> {{ message }}

腳本學習1列出一組IP內所有活動主機

cat -c spa ash 活動 pin null status amp 1、用ping命令編寫腳本來查詢一組IP地址同時檢查他們是否處於活動狀態 1 [zhi@centos7 ~]$ vi CheckHostStatus.sh 2 [zhi@centos7 ~]$

Python爬蟲學習1

數據 bin des fin load 寫入 all pytho urlopen 接觸python不久,也在慕課網學習了一些python相關基礎,對於爬蟲初步認為是依靠一系列正則獲取目標內容數據 於是參照著慕課網上的教學視頻,完成了我的第一個python爬蟲,雞凍 >

Docker學習1安裝

www. ati https 機制 生產環境 開機啟動 works tab 流行 1. Docker簡介 Docker 是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,然後發布到任何流行的 Linux 機器上,也可以實現虛擬化。容器是完全