1. 程式人生 > >Linux 串列埠驅動相關

Linux 串列埠驅動相關

Linux串列埠驅動相關主要涉及3個重要的結構體,uart_driver,uart_port,uart_ops。本文主要以msm8917平臺分析, 先貼dts相關程式碼

	blsp1_uart2: [email protected]78b0000 {
		compatible = "qcom,msm-lsuart-v14";
		reg = <0x78b0000 0x200>;
		interrupts = <0 108 0>;
		status = "disabled";
		clocks = <&clock_gcc clk_gcc_blsp1_uart2_apps_clk>
, <&clock_gcc clk_gcc_blsp1_ahb_clk>; clock-names = "core_clk", "iface_clk"; };

幾個重要結構體定義

uart_driver封裝了tty_driver,使底層uart驅動不用關心tty_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()的操作。
uart_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; };

對應高通平臺的程式碼

static struct uart_driver msm_hsl_uart_driver = {
	.owner = THIS_MODULE,
	.driver_name = "msm_serial_hsl",
	.dev_name = "ttyHSL",
	.nr = UART_NR,
	.cons = MSM_HSL_CONSOLE,
};

其中uart_driver中的uart_state比較重要,在uart_register_driver中為state分配了記憶體

/*
 * This is the state information which is persistent across opens.
 */
struct uart_state {
	struct tty_port		port;

	enum uart_pm_state	pm_state;
	struct circ_buf		xmit;

	struct uart_port	*uart_port;
};

uart_port用於描述一個UART埠(直接對應於一個串列埠)的I/O埠或I/O記憶體地址、FIFO大小、埠型別等資訊。串列埠核心層提供如下函式來新增1個埠:

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

uart_port定義

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);
	void			(*set_termios)(struct uart_port *,
				               struct ktermios *new,
				               struct ktermios *old);
	int			(*startup)(struct uart_port *port);
	void			(*shutdown)(struct uart_port *port);
	void			(*throttle)(struct uart_port *port);
	void			(*unthrottle)(struct uart_port *port);
	int			(*handle_irq)(struct uart_port *);
	void			(*pm)(struct uart_port *, unsigned int state,
				      unsigned int old);
	void			(*handle_break)(struct uart_port *);
	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 and RT288x type IO */
#define UPIO_TSI		(5)			/* Tsi108/109 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

	/* flags must be updated while holding port mutex */
	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))
/* Port has hardware-assisted h/w flow control (iow, auto-RTS *not* auto-CTS) */
#define UPF_HARD_FLOW		((__force upf_t) (1 << 21))
/* Port has hardware-assisted s/w flow control */
#define UPF_SOFT_FLOW		((__force upf_t) (1 << 22))
#define UPF_CONS_FLOW		((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ		((__force upf_t) (1 << 24))
#define UPF_EXAR_EFR		((__force upf_t) (1 << 25))
#define UPF_BUG_THRE		((__force upf_t) (1 << 26))
/* 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))

	/* status must be updated while holding port lock */
	upstat_t		status;

#define UPSTAT_CTS_ENABLE	((__force upstat_t) (1 << 0))
#define UPSTAT_DCD_ENABLE	((__force upstat_t) (1 << 1))

	int			hw_stopped;		/* sw-assisted CTS flow state */
	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		irq_wake;
	unsigned char		unused[2];
	struct attribute_group	*attr_group;		/* port specific attributes */
	const struct attribute_group **tty_groups;	/* all attributes (serial core use only) */
	void			*private_data;		/* generic platform data pointer */
};

對應高通平臺的程式碼

struct msm_hsl_port {
	struct uart_port	uart;
	char			name[16];
	struct clk		*clk;
	struct clk		*pclk;
	struct dentry		*loopback_dir;
	unsigned int		imr;
	unsigned int		*uart_csr_code;
	unsigned int            *gsbi_mapbase;
	unsigned int            *mapped_gsbi;
	unsigned int            old_snap_state;
	unsigned long		ver_id;
	int			tx_timeout;
	struct mutex		clk_mutex;
	enum uart_core_type	uart_type;
	enum uart_func_mode	func_mode;
	struct wakeup_source	port_open_ws;
	int			clk_enable_count;
	u32			bus_perf_client;
	/* BLSP UART required BUS Scaling data */
	struct msm_bus_scale_pdata *bus_scale_table;
};

msm_hsl_port 對uart_port做了一個封裝

static struct msm_hsl_port msm_hsl_uart_ports[] = {
	{
		.uart = {
			.iotype = UPIO_MEM,
			.ops = &msm_hsl_uart_pops,
			.flags = UPF_BOOT_AUTOCONF,
			.fifosize = 64,
			.line = 0,
		},
	},
	{
		.uart = {
			.iotype = UPIO_MEM,
			.ops = &msm_hsl_uart_pops,
			.flags = UPF_BOOT_AUTOCONF,
			.fifosize = 64,
			.line = 1,
		},
	},
	{
		.uart = {
			.iotype = UPIO_MEM,
			.ops = &msm_hsl_uart_pops,
			.flags = UPF_BOOT_AUTOCONF,
			.fifosize = 64,
			.line = 2,
		},
	},
};

uart_ops

/*
 * This structure describes all the operations that can be done on the
 * physical hardware.  See Documentation/serial/driver for details.
 */
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		(*throttle)(struct uart_port *);
	void		(*unthrottle)(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 *, int new);
	void		(*pm)(struct uart_port *, unsigned int state,
			      unsigned int oldstate);
	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
	int		(*poll_init)(struct uart_port *);
	void		(*poll_put_char)(struct uart_port *, unsigned char);
	int		(*poll_get_char)(struct uart_port *);
#endif
};

高通平臺對應程式碼

static struct uart_ops msm_hsl_uart_pops = {
	.tx_empty = msm_hsl_tx_empty,
	.set_mctrl = msm_hsl_set_mctrl,
	.get_mctrl = msm_hsl_get_mctrl,
	.stop_tx = msm_hsl_stop_tx,
	.start_tx = msm_hsl_start_tx,
	.stop_rx = msm_hsl_stop_rx,
	.enable_ms = msm_hsl_enable_ms,
	.break_ctl = msm_hsl_break_ctl,
	.startup = msm_hsl_startup,
	.shutdown = msm_hsl_shutdown,
	.set_termios = msm_hsl_set_termios,
	.type = msm_hsl_type,
	.release_port = msm_hsl_release_port,
	.request_port = msm_hsl_request_port,
	.config_port = msm_hsl_config_port,
	.verify_port = msm_hsl_verify_port,
	.pm = msm_hsl_power,
};

驅動註冊流程

在使用串列埠核心層這個通用串列埠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驅動的主體工作。

串列埠驅動初始化過程

在高通串列埠驅動的模組載入函式(msm_serial_hsl_init)中會呼叫uart_register_driver()註冊msm_hsl_uart_driver這個uart_driver(對應driver)
同時經過msm_serial_hsl_init()–>platform_driver_register(&msm_hsl_platform_driver);–>msm_serial_hsl_probe–>初始化UART埠並呼叫uart_add_one_port()新增埠。(對應device)

下面開始具體分析,從uart_register_driver開始,對應程式碼在serial_core.c中

/**                                                                                         
 *  uart_register_driver - register a driver with the uart core layer                       
 *  @drv: low level driver structure                                                        
 *                                                                                          
 *  Register a uart driver with the core driver.  We in turn register                       
 *  with the tty layer, and initialise the core driver per-port state.                      
 *                                                                                          
 *  We have a proc file in /proc/tty/driver which is named after the                        
 *  normal driver.                                                                          
 *                                                                                          
 *  drv->port should be NULL, and the per-port structures should be                         
 *  registered using uart_add_one_port after this call has succeeded.                       
 */                                                                                         
int uart_register_driver(struct uart_driver *drv)                                           
{                                                                                           
    struct tty_driver *normal;                                                              
    int i, retval;                                                                          
                                                                                            
    BUG_ON(drv->state);                                                                     
                                                                                            
    /*                                                                                      
     * Maybe we should be using a slab cache for this, especially if                        
     * we have a large number of ports to handle.                                           
     */                                                                                     
    drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);                  
    if (!drv->state)                                                                        
        goto out;                                                                           
                                                                                            
    normal = alloc_tty_driver(drv->nr);                                                     
    if (!normal)                                                                            
        goto out_kfree;                                                                     
                                                                                            
    drv->tty_driver = normal;                                                               
                                                                                            
    normal->driver_name = drv->driver_name;                                                 
    normal->name        = drv->dev_name;                                                    
    normal->major       = drv->major;                                                       
    normal->minor_start = drv->minor;                                                       
    normal->type        = TTY_DRIVER_TYPE_SERIAL;                                           
    normal->subtype     = SERIAL_TYPE_NORMAL;                                               
    normal->init_termios    = tty_std_termios;                                              
    normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;                    
    normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;                   
    normal->flags       = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;                     
    normal->driver_state    = drv;                                                          
    tty_set_operations(normal, &uart_ops);    //會呼叫到此ops中有uart_open等
                                                          
    /*                                                    
     * Initialise the UART state(s).                      
     */                                                   
    for (i = 0; i < drv->nr; i++) {                       
        struct uart_state *state = drv->state + i;        
        struct tty_port *port = &state->port;             
                                                          
        tty_port_init(port);                              
        port->ops = &uart_port_ops;                       
        port->close_delay     = HZ / 2; /* .5 seconds */  
        port->closing_wait    = 30 * HZ;/* 30 seconds */  
    }                                                     
                                                          
    retval = tty_register_driver(normal);                 
    if (retval >= 0)                                      
        return retval;                                    
                                                          
    for (i = 0; i < drv->nr; i++)                         
        tty_port_destroy(&drv->state[i].port);            
    put_tty_driver(normal);                               
out_kfree:                                                
    kfree(drv->state);                                    
out:                                                      
    return -ENOMEM;                                       
}                                                                                                       

貼兩個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
            
           

相關推薦

Linux 串列驅動相關

Linux串列埠驅動相關主要涉及3個重要的結構體,uart_driver,uart_port,uart_ops。本文主要以msm8917平臺分析, 先貼dts相關程式碼 blsp1_uart2: [email protected]78b0000 { compatible

6410 實現 linux 串列驅動詳解

為了實現串列埠通訊,需要在嵌入式linux下編寫相應的驅動程式。在嵌入式系統中,串列埠被看做終端裝置tty。終端裝置是unix體系中一個非常重要的物件,內容非常複雜,它是整個unix人機互動的基礎,其地位並不亞於檔案系統在作業系統中的作用。筆者muge0913在此對uar

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

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

Linux串列驅動分析初始化

<pre name="code" class="cpp">/** * uart分析 * * 其實串列埠分析就兩個重要的檔案: S3c2440.c Samsung.c * * **/ /*1. 首先從Samsung.c的模組初始化函式看起*/ s

Linux串列程式設計詳解 linux串列相關設定函式

tcgetattr    函式用於獲取與終端相關的引數。引數fd為終端的檔案描述符,返回的結果儲存在termios 結構體中 http://baike.baidu.com/view/5644808.htm?fr=aladdin tcset

linux使用USB轉串列驅動設定

【一】、驅動相關說明: 如果直接使用串列埠線,而沒有用到USB轉串列埠裝置,就不需要安裝驅動。 如果使用了USB轉串列埠,一般情況下也不需要安裝驅動了,目前linux系統已經包含了該驅動,可以自動識別,亦可通過以下命令檢視以便確認是否支援。 檢視模組裝載的情況: 引用 lsmod |

linux核心中串列驅動註冊過程(tty驅動)

原文轉自:http://m.blog.csdn.net/blog/lushengchu2003/9368031 最近閒來無事情做,想到以前專案中遇到串列埠硬體流控制的問題,藍芽串列埠控制返回錯誤,上層讀寫串列埠buffer溢位的問題等,也折騰了一陣子,雖然 最終證明與串列埠驅動無關,但是排查問題

Linux/Android系統開發 串列驅動原始碼,FIFO模式

該驅動適用於採用linux和android系統平臺的C/C++串列埠開發。 FIFO傳送模式:建立資料傳送FIFO佇列,在多工資料傳送情境下,既能保證資料傳送任務能夠得到執行,又可解決資料傳送衝突問題。 select接收資料:有效監聽串列埠接收資料,提高執行效率,減少出錯

Linux下USB轉串列驅動

Linux發行版自帶usb to serial驅動,以模組方式編譯驅動,在核心原始碼目錄下執行Make MenuConfig選擇Devces drivers-->USB seupport--> <M>USB Serial Converter support --> <M

linux UART串列驅動開發

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

Linux串列(serial、uart)驅動程式設計

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

linux下的串列驅動程式

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

8250 driver Linux串列驅動解析 xr16v554

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

Linux核心移植 part3:串列驅動

每日一樂:小美在作文簿裡寫上長大後的願望:一、我希望能有一個可愛的孩子;二、我還希望能有一個愛我的丈夫。結果,發現老師寫了一句評語:“請注意先後順序。” 介紹Linux kernel 4.1對exynos 4412串列埠的支援。 其實這部分工作已經過

ARM Linux下安裝CH341串列驅動

在arm-Linux環境下安裝CH341串列埠驅動需要單獨編譯串列埠的驅動。本人編譯環境Ubuntu 14.04gcc編譯工具arm-linux-gnueabihf-gcc。1.程式碼檢查    檢視核心目錄下 kernel/drivers/usb/serial/ch341.

Linux串列程式設計教程(三)——串列程式設計詳(原始碼)解:http://blog.csdn.net/u011192270/article/details/48174353 Linux下的串列程式設計(二)----(圖文並茂,講解深刻)http://blog.csdn.net/w28252

Linux串列埠程式設計教程(三)——串列埠程式設計詳(原始碼)解:http://blog.csdn.net/u011192270/article/details/48174353 Linux下的串列埠程式設計(二)----(圖文並茂,講解深刻)http://blog.csdn.ne

Linux串列程式設計

串列埠通訊是指一次只傳送一個數據位。雖然在通訊的時候串列埠有 8 位或者 9 位等,但是在物理層面傳輸的時候,它仍然是以單個 bit 的方式傳輸的 一般特指 RS232 標準的介面 在 linux 下串列埠程式設計流程如下: 開啟串列埠 核心是用op

MTK串列驅動開發

MTK串列埠驅動開發 由於最近在工作中需要使用MTK的MT6261進行移動嵌入式裝置的開發,所以將MTK串列埠驅動開發流程貼出來分享給大家。 1.使用串列埠工具配置UART管腳,此處配置的是UART2開啟原始碼目錄下的\custom\drv\Drv_Tool\DrvGen.exe

ubuntu安裝USB轉串列驅動(PL2303)

在Ubuntu下利用minicom進行嵌入式開發時可能會用到USB轉串列埠,這時就會用到USB轉串列埠驅動,以前的Ubuntu是直接將此驅動編譯進核心,但不知道從哪個版本開始Ubuntu將其從核心去掉了,所以要用到Ubuntu的minicom時只能由我們自己安裝USB轉串列埠驅動,方法如下:

ITOP4412裸機程式設計-串列驅動

文章目錄 前言: 原理分析: 原始碼:        修改main.S        修改exynos4412.h