1. 程式人生 > >linux下的串列埠驅動程式

linux下的串列埠驅動程式

串列埠驅動確實不簡單,不過多花費心思整體思路還是容易理清的。

原文如下:

一、核心資料結構
串列埠驅動有3個核心資料結構,它們都定義在<#include linux/serial_core.h>
1、uart_driver
uart_driver包含了串列埠裝置名、串列埠驅動名、主次裝置號、串列埠控制檯(可選)等資訊,還封裝了tty_driver(底層串列埠驅動無需關心tty_driver)。



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


2、uart_port
uart_port用於描述串列埠埠的I/O埠或I/O記憶體地址、FIFO大小、埠型別、串列埠時鐘等資訊。實際上,一個uart_port例項對應一個串列埠裝置

struct uart_port {
    spinlock_t             lock;           /* 串列埠埠鎖 */
    unsigned int           iobase;         /* IO埠基地址 */
    unsigned char __iomem *membase;        /* IO記憶體基地址,經對映(如ioremap)後的IO記憶體虛擬基地址 */
    unsigned int           irq;            /* 中斷號 */
    unsigned int           uartclk;        /* 串列埠時鐘 */
    unsigned int           fifosize;       /* 串列埠FIFO緩衝大小 */
    unsigned char          x_char;         /* xon/xoff字元 */
    unsigned char          regshift;       /* 暫存器位移 */
    unsigned char          iotype;         /* IO訪問方式 */
    unsigned char          unused1;

#define UPIO_PORT        (0)               /* IO埠 */
#define UPIO_HUB6        (1)
#define UPIO_MEM         (2)               /* IO記憶體 */
#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;  /* 關心的Rx error status */
    unsigned int        ignore_status_mask;/* 忽略的Rx error status */
    struct uart_info      *info;           /* pointer to parent info */
    struct uart_icount     icount;         /* 計數器 */

    struct console        *cons;           /* console結構體 */
#ifdef CONFIG_SERIAL_CORE_CONSOLE
    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_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))
#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;        /* 當前的moden設定 */
    unsigned int             timeout;      /* character-based timeout */        
    unsigned int             type;         /* 埠型別 */
    const struct uart_ops   *ops;          /* 串列埠埠操作函式集 */
    unsigned int             custom_divisor;
    unsigned int             line;         /* 埠索引 */
    resource_size_t          mapbase;      /* IO記憶體物理基地址,可用於ioremap */
    struct device           *dev;          /* 父裝置 */
    unsigned char            hub6;         /* this should be in the 8250 driver */
    unsigned char            suspended;
    unsigned char            unused[2];
    void                    *private_data; /* 埠私有資料,一般為platform資料指標 */
};
 

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_info有兩個成員在底層串列埠驅動會用到:xmit和tty。使用者空間程式通過串列埠傳送資料時,上層驅動將使用者資料儲存在xmit;而串列埠傳送中斷處理函式就是通過xmit獲取到使用者資料並將它們傳送出去。串列埠接收中斷處理函式需要通過tty將接收到的資料傳遞給行規則層。


/* uart_info例項僅在串列埠埠開啟時有效,它可能在串列埠關閉時被串列埠核心層釋放。因此,在使用uart_port的uart_info成員時必須保證串列埠已開啟。底層驅動和核心層驅動都可以修改uart_info例項。
 * This is the state information which is only valid when the port
 * is open; it may be freed by the core driver once the device has
 * been closed. Either the low level driver or the core can modify
 * stuff here.
 */
struct uart_info {
    struct tty_struct     *tty;
    struct circ_buf        xmit;
    uif_t                  flags;

/*
 * Definitions for info->flags. These are _private_ to serial_core, and
 * are specific to this structure. They may be queried by low level drivers.
 */
#define UIF_CHECK_CD        ((__force uif_t) (1 << 25))
#define UIF_CTS_FLOW        ((__force uif_t) (1 << 26))
#define UIF_NORMAL_ACTIVE    ((__force uif_t) (1 << 29))
#define UIF_INITIALIZED        ((__force uif_t) (1 << 31))
#define UIF_SUSPENDED        ((__force uif_t) (1 << 30))

    int                     blocked_open;

    struct tasklet_struct   tlet;

    wait_queue_head_t       open_wait;
    wait_queue_head_t       delta_msr_wait;
};
 


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         (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios*old); /* 設定串列埠引數 */
    void         (*pm)(struct uart_port *, unsigned int state,
             unsigned int oldstate); /* 串列埠電源管理 */
    int          (*set_wake)(struct uart_port *, unsigned int state); /*  */
    const char  *(*type)(struct uart_port *); /* 返回一描述串列埠型別的字串 */
    void         (*release_port)(struct uart_port *); /* 釋放串列埠已申請的IO埠/IO記憶體資源,必要時還需iounmap */
    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); /* IO控制 */
};
 


二、串列埠驅動API
1、uart_register_driver
/* 功能:    uart_register_driver用於將串列埠驅動uart_driver註冊到核心(串列埠核心層)中,通常在模組初始化函式呼叫該函式。
 * 引數 drv:要註冊的uart_driver

 * 返回值:  成功,返回0;否則返回錯誤碼
 */
int uart_register_driver(struct uart_driver *drv)
 

2、uart_unregister_driver
/* 功能:    uart_unregister_driver用於登出我們已註冊的uart_driver,通常在模組解除安裝函式呼叫該函式
 * 引數 drv:要登出的uart_driver

 * 返回值:  成功,返回0;否則返回錯誤碼
 */
void uart_unregister_driver(struct uart_driver *drv)
 

3、uart_add_one_port
/* 功能:    uart_add_one_port用於為串列埠驅動新增一個串列埠埠,通常在探測到裝置後(驅動的裝置probe方法)呼叫該函式
 * 引數 drv:串列埠驅動
 *      port:要新增的串列埠埠

 * 返回值:  成功,返回0;否則返回錯誤碼
 */
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
 

4、uart_remove_one_port
/* 功能:     uart_remove_one_port用於刪除一個已新增到串列埠驅動中的串列埠埠,通常在驅動解除安裝時呼叫該函式
 * 引數 drv: 串列埠驅動
 *      port: 要刪除的串列埠埠
 * 返回值:   成功,返回0;否則返回錯誤碼
 */
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)
 

5、uart_write_wakeup
/* 功能:     uart_write_wakeup喚醒上層因向串列埠埠寫資料而阻塞的程序,通常在串列埠傳送中斷處理函式中呼叫該函式
 * 引數 port:需要喚醒寫阻塞程序的串列埠埠
 */
void uart_write_wakeup(struct uart_port *port)
 

6、uart_suspend_port
/* 功能:     uart_suspend_port用於掛起特定的串列埠埠
 * 引數 drv: 要掛起的串列埠埠所屬的串列埠驅動
 *      port:要掛起的串列埠埠
 * 返回值:   成功返回0;否則返回錯誤碼
 */
int uart_suspend_port(struct uart_driver *drv, struct uart_port *port)
 

7、uart_resume_port
/* 功能:     uart_resume_port用於恢復某一已掛起的串列埠
 * 引數 drv: 要恢復的串列埠埠所屬的串列埠驅動
 *      port:要恢復的串列埠埠
 * 返回值:   成功返回0;否則返回錯誤碼
 */
int uart_resume_port(struct uart_driver *drv, struct uart_port *port)
 

8、uart_get_baud_rate
/* 功能:        uart_get_baud_rate通過解碼termios結構體來獲取指定串列埠的波特率
 * 引數 port:  要獲取波特率的串列埠埠
 *     termios:當前期望的termios配置(包含串列埠波特率)
 *     old:    以前的termios配置,可以為NULL
 *     min:    可接受的最小波特率
 *     max:    可接受的最大波特率
 * 返回值:     串列埠的波特率
 */
unsigned int
uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
     struct ktermios *old, unsigned int min, unsigned int max)
 

9、uart_get_divisor
/* 功能:     uart_get_divisor用於計算某一波特率的串列埠時鐘分頻數(串列埠波特率除數)
 * 引數 port:要計算時鐘分頻數的串列埠埠
 *      baud:期望的波特率
 *返回值:    串列埠時鐘分頻數
 */
unsigned int uart_get_divisor(struct uart_port *port, unsigned int baud)
 

10、uart_update_timeout
/* 功能:      uart_update_timeout用於更新(設定)串列埠FIFO超時時間
 * 引數 port: 要更新超時時間的串列埠埠
 *     cflag:termios結構體的cflag值
 *     baud: 串列埠的波特率
 */
void uart_update_timeout(struct uart_port *port, unsigned int cflag, unsigned int baud)
 

11、uart_match_port
/* 功能:uart_match_port用於判斷兩串列埠埠是否為同一埠
 * 引數 port1、port2:要判斷的串列埠埠
 * 返回值:不同返回0;否則返回非0
 */
int uart_match_port(struct uart_port *port1, struct uart_port *port2)
 

12、uart_console_write

/* 功能:        uart_console_write用於向串列埠埠寫一控制檯資訊

 * 引數 port:    要寫資訊的串列埠埠
 *     s:       要寫的資訊
 *     count:   資訊的大小
 *     putchar: 用於向串列埠埠寫字元的函式,該函式函式有兩個引數:串列埠埠和要寫的字元
 */
void uart_console_write(struct uart_port *port, const char *s,
            unsigned int count,
            void (*putchar)(struct uart_port *, int))
 

 
三、串列埠驅動例子
該串列埠驅動例子是我針對s3c2410處理器的串列埠2(uart2)獨立開發的。因為我通過博創2410s開發板的GRPS擴充套件板來測試該驅動(已通過測試),所以我叫該串列埠為gprs_uart。
 
該驅動將串列埠看作平臺(platform)裝置。platform可以看作一偽匯流排,用於將集成於片上系統的輕量級裝置與Linux裝置驅動模型聯絡到一起,它包含以下兩部分(有關platform的宣告都在#include <linux/platform_device.h>,具體實現在drivers/base/platform.c):
1、platform裝置。我們需要為每個裝置定義一個platform_device例項
struct platform_device {
    const char      *name;         /* 裝置名 */
    int              id;           /* 裝置的id號 */
    struct device    dev;          /* 其對應的device */
    u32              num_resources;/* 該裝置用有的資源數 */
    struct resource *resource;     /* 資源陣列 */
};
 

為我們的裝置建立platform_device例項有兩種方法:填充一個platform_device結構體後用platform_device_register(一次註冊一個)或platform_add_devices(一次可以註冊多個platform裝置)將platform_device註冊到核心;更簡單的是使用platform_device_register_simple來建立並註冊我們的platform_device。
2、platform驅動。platform裝置由platform驅動進行管理。當裝置加入到系統中時,platform_driver的probe方法會被呼叫來見對應的裝置新增或者註冊到核心;當裝置從系統中移除時,platform_driver的remove方法會被呼叫來做一些清理工作,如移除該裝置的一些例項、登出一些已註冊到系統中去的東西。
struct platform_driver {
    int  (*probe)(struct platform_device *);
    int  (*remove)(struct platform_device *);
    void (*shutdown)(struct platform_device *);
    int  (*suspend)(struct platform_device *, pm_message_t state);
    int  (*suspend_late)(struct platform_device *, pm_message_t state);
    int  (*resume_early)(struct platform_device *);
    int  (*resume)(struct platform_device *);
    struct device_driver driver;
};
 

更詳細platform資料可參考網上相關文章。

例子驅動中申請和釋放IO記憶體區的整個過程如下:

insmod gprs_uart.ko→gprs_init_module()→uart_register_driver()→gprs_uart_probe()→ uart_add_one_port()→gprs_uart_config_port()→gprs_uart_request_port()→request_mem_region()

rmmod gprs_uart.ko→gprs_exit_module()→uart_unregister_driver()→gprs_uart_remove()→uart_remove_one_port()→gprs_uart_release_port()→release_mem_region()

例子驅動中申請和釋放IRQ資源的整個過程如下:

open /dev/gprs_uart→gprs_uart_startup()→request_irq()

close /dev/gprs_uart→gprs_uart_shutdown()→free_irq()

相關推薦

linux串列驅動程式

串列埠驅動確實不簡單,不過多花費心思整體思路還是容易理清的。原文如下:一、核心資料結構 串列埠驅動有3個核心資料結構,它們都定義在<#include linux/serial_core.h> 1、uart_driver uart_driver包含了串列埠裝置名、

Linux串列驅動程式(2)-串列驅動程式初始化分析

1、串列埠驅動程式結構分析 對使用者來講,能夠正常使用串列埠肯定是需要實現如下函式的: 1、串列埠裝置檔案的開啟 2、串列埠裝置檔案的初始化 3、串列埠裝置檔案的讀寫 4、串列埠裝置檔案的控制 2、串列埠驅動中重要的資料結構 首先分析一下串列埠讀寫的流程 當用戶讀寫串列埠

linux裝置驅動,tty串列程式設計 如何檢視linux串列是否可用?串列名稱等

如何檢視linux下串列埠是否可用?串列埠名稱等? 檢視串列埠是否可用,可以對串列埠傳送資料比如對com1口,echo lyjie126 > /dev/ttyS0 檢視串列埠名稱使用 ls -l /dev/ttyS* 一般情況下串列埠的名稱全部在dev下面,如果你沒

解決Linux串列資料接收不全的異常問題

1、引言     最近在Linux下除錯串列埠程式,遇到了串列埠資料接收不全的異常問題,經過將近一上午的努力終於找到問題根源,特此分享給大家,此次除錯過程中用到了主要用到了minicom工具,至於minicom的使用大家可以自行查詢相關資料。 2、正文  

STM32 UART串列驅動程式

文章原始地址: http://feotech.com/?p=56 示例1.通過UART1進行資料傳送 UART 1 的初始化 /** * @brief UART1 Initialise. * @param None. * @retval None. */ void UART

記一次linux串列資料丟包解決過程

專案中兩個晶片之間用串列埠進行通訊,由於傳輸格式中有校驗位,在資料量很大的時候總是校驗失敗。於是花了很長的時間最終解決了這個問題。 首先串列埠丟資料有兩種情況(明顯排除傳送端傳送的資料不對),第一種是通道也就是串列埠線或者連線口不行,無法承受很高的波特率(我使用的波特率是9

linux 串列工具minicom的使用

為了在ubuntu 12.04下能夠看到正常串列埠輸出,使用Minicom. 1、安裝   sudo apt-get install minicom 2、配置 (1)首先在命令列下執行sudo minicom啟動程式 (2)進入主介面後按 Ctrl+A 然後再按Z 進入配

linux UART串列驅動開發

內容簡介: 介紹了Linux下的串列埠驅動的設計層次及介面, 並指出串列埠與TTY終端之間的關聯層次(串列埠可作TTY終端使用), 以及Linux下的中斷處理機制/中斷共享機制, 還有串列埠緩衝機制當中涉及的軟中斷機制; 其中有關w83697/w83977 IC方面的知識, 具體參考相關手冊, 對串列埠的配

LinuxPCI裝置驅動程式開發基本框架

PCI是一種廣泛採用的匯流排標準,它提供了許多優於其它匯流排標準(如EISA)的新特性,目前已經成為計算機系統中應用最為廣泛,並且最為通用的匯流排標準。Linux的核心能較好地支援PCI匯流排,本文以Intel 386體系結構為主,探討了在Linux下開發PCI裝置驅動程式的基本框架。    一、PCI匯流排

linux串列除錯工具/串列終端推薦: picocom

對於picocom, kermit, minicom, picocom 最簡單易用,也完全符合我的使用需求。 安裝(mint / ubuntu):$ sudo apt-get install picocom使用:$ picocom -b 115200 /dev/ttyUSB

嵌入式Linux串列除錯

By Toradex秦海 1). 簡介 UART串列埠是嵌入式裝置最為常用的除錯和通訊介面之一,無論是RS232還是RS422/485都有著非常廣泛的應用,因此本文就基於嵌入式Linux演示在User Space進行串列埠除錯。 本文所演示的平臺來自於Toradex Co

linux串列非標準波特率的實現

最近要在linux下使用電腦的串列埠,而使用的波特率不是諸如9600,19200之類的標準波特率,從網上查詢非標準波特率的設定方法,都是說對termios結構體進行設定。按照網上的程式碼進行設定卻不成功。費了很大力氣終於找到了原因:原來我的linux版本支援一部分非標準的

LINUXADC按鍵驅動程式

ADC按鍵驅動 Adc鍵盤原理圖如下,將串聯電阻之間分別用按鍵引出來與地相連,當按鍵按下時端電壓會發生改變。基本思想是在ADC驅動基礎上,對取樣電壓進行判斷,檢測是哪一個按鍵按下。 1.      ADC驅動分析 在init()函式中,首先獲取adc的時鐘,並用clk_e

8250 driver Linux串列驅動解析 xr16v554

一:前言 前一段時間自己實踐了一下8250晶片串列埠驅動的編寫。今天就在此基礎上分析一下 linux kernel  自帶的串列埠驅動。畢竟只有對比專業的驅動程式碼才能更好的進步, 同以往一樣,基於linux kernel2.6.25. 相應驅動程式碼位於

Cutecom--Linux串列除錯工具

Cutecom是一款在Linux環境下,非常好用的串列埠除錯工具,使用方法幾乎跟Windows下的串列埠除錯工具一樣,非常方便。 Cutecom的安裝:直接在終端輸入:sudo apt-get install cutecom。 安裝完後,使用命令:sudo cutecom開

Linux串列程式設計總結

1.串列埠操作需要的標頭檔案 #include <stdio.h> //標準輸入輸出定義 #include <stdlib.h> //標準函式庫定義 #include <unistd.h> //Unix標準函式定義 #include <

linux串列工具minicom使用

系統環境:ubuntu destop 11.10 我當時的需要主要是兩個,能夠看到正常串列埠輸出,並且把串列埠內容實時輸出到檔案中 那接下來工作主要是兩個:1、安裝 2、配置 相信各位也都是至少會操作一些全令行的,我們在這裡用apt-get安裝(不會的同學請google一

linux串列轉TCP網路通訊

“` include”test.h” include”modbus.h” define BUFFER_SIZE 29 int ret; modbus_t *mb; int16_t tab_reg[32]={0}; //初始化串列

linux串列控制

/*  本程式符合GPL條約  *  Beneboy 2003-5-16 */ #include <stdio.h>              // printf #include <fcntl.h>              // open #in

Linux串列通訊詳解(上)開啟串列串列初始化詳解

linux下串列埠通訊主要有下面幾個步驟 串列埠通訊流程圖 下面我會一一介紹這幾個步驟。 1.開啟串列埠 程式碼(串列埠為ttyUSB0) //開啟串列埠 int open_port(void) { int fd; fd=open("/dev/ttyUSB0