Linux串口(serial、uart)驅動程序設計正文
一、核心數據結構
串口驅動有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()
想了解更詳細的調用過程可以在驅動模塊各函數頭插入printk(KERN_DEBUG "%s\n", __FUNCTION__);並在函數尾插入printk(KERN_DEBUG "%s done\n", __FUNCTION__);
下面是串口驅動例子和其GPRS測試程序源碼下載地址:
http://www.pudn.com/downloads258/sourcecode/unix_linux/detail1192104.html # include < linux / module . h >
# include < linux / init . h >
# include < linux / kernel . h > /* printk() */
# include < linux / slab . h > /* kmalloc() */
# include < linux / fs . h > /* everything... */
# include < linux / errno . h > /* error codes */
# include < linux / types . h > /* size_t */
# include < linux / fcntl . h > /* O_ACCMODE */
# include < asm / system . h > /* cli(), *_flags */
# include < asm / uaccess . h > /* copy_*_user */
# include < linux / ioctl . h >
# include < linux / device . h >
# include < linux / platform_device . h >
# include < linux / sysrq . h >
# include < linux / tty . h >
# include < linux / tty_flip . h >
# include < linux / serial_core . h >
# include < linux / serial . h >
# include < linux / delay . h >
# include < linux / clk . h >
# include < linux / console . h >
# include < asm / io . h >
# include < asm / irq . h >
# include < asm / hardware . h >
# include < asm / plat - s3c / regs - serial . h >
# include < asm / arch / regs - gpio . h >
# define DEV_NAME "gprs_uart" /* 設備名 */
/* 這裡將串口的主設備號設為0,則串口設備編號由內核動態分配;你也可指定串口的設備編號 */
# define GPRS_UART_MAJOR 0 /* 主設備號 */
# define GPRS_UART_MINOR 0 /* 次設備號 */
# define GPRS_UART_FIFO_SIZE 16 /* 串口FIFO的大小 */
# define RXSTAT_DUMMY_READ ( 0x10000000 )
# define MAP_SIZE ( 0x100 ) /* 要映射的串口IO內存區大小 */
/* 串口發送中斷號 */
# define TX_IRQ ( port ) ( ( port ) - > irq + 1 )
/* 串口接收中斷號 */
# define RX_IRQ ( port ) ( ( port ) - > irq )
/* 允許串口接收字元的標誌 */
# define tx_enabled ( port ) ( ( port ) - > unused [ 0 ] )
/* 允許串口發送字元的標誌 */
# define rx_enabled ( port ) ( ( port ) - > unused [ 1 ] )
/* 獲取寄存器地址 */
# define portaddr ( port , reg ) ( ( port ) - > membase + ( reg ) )
/* 讀8位寬的寄存器 */
# define rd_regb ( port , reg ) ( ioread8 ( portaddr ( port , reg ) ) )
/* 讀32位寬的寄存器 */
# define rd_regl ( port , reg ) ( ioread32 ( portaddr ( port , reg ) ) )
/* 寫8位寬的寄存器 */
# define wr_regb ( port , reg , val ) \
do { iowrite8 ( val , portaddr ( port , reg ) ) ; } while ( 0 )
/* 寫32位寬的寄存器 */
# define wr_regl ( port , reg , val ) \
do { iowrite32 ( val , portaddr ( port , reg ) ) ; } while ( 0 )
/* 禁止串口發送數據 */
static void gprs_uart_stop_tx ( struct uart_port * port )
{
if ( tx_enabled ( port ) ) /* 若串口已啟動發送 */
{
disable_irq ( TX_IRQ ( port ) ) ; /* 禁止發送中斷 */
tx_enabled ( port ) = 0 ; /* 設置串口為未啟動發送 */
}
}
/* 使能串口發送數據 */
static void gprs_uart_start_tx ( struct uart_port * port )
{
if ( ! tx_enabled ( port ) ) /* 若串口未啟動發送 */
{
enable_irq ( TX_IRQ ( port ) ) ; /* 使能發送中斷 */
tx_enabled ( port ) = 1 ; /* 設置串口為已啟動發送 */
}
}
/* 禁止串口接收數據 */
static void gprs_uart_stop_rx ( struct uart_port * port )
{
if ( rx_enabled ( port ) ) /* 若串口已啟動接收 */
{
disable_irq ( RX_IRQ ( port ) ) ; /* 禁止接收中斷 */
rx_enabled ( port ) = 0 ; /* 設置串口為未啟動接收 */
}
}
/* 使能modem的狀態信號 */
static void gprs_uart_enable_ms ( struct uart_port * port )
{
}
/* 串口的Tx FIFO緩存是否為空 */
static unsigned int gprs_uart_tx_empty ( struct uart_port * port )
{
int ret = 1 ;
unsigned long ufstat = rd_regl ( port , S3C2410_UFSTAT ) ;
unsigned long ufcon = rd_regl ( port , S3C2410_UFCON ) ;
if ( ufcon & S3C2410_UFCON_FIFOMODE ) /* 若使能了FIFO */
{
if ( ( ufstat & S3C2410_UFSTAT_TXMASK ) ! = 0 | | /* 0 <FIFO <=15 */
( ufstat & S3C2410_UFSTAT_TXFULL ) ) /* FIFO滿 */
ret = 0 ;
}
else /* 若未使能了FIFO,則判斷發送緩存和發送移位寄存器是否均為空 */
{
ret = rd_regl ( port , S3C2410_UTRSTAT ) & S3C2410_UTRSTAT_TXE ;
}
return ret ;
}
/* 獲取串口modem控制,因為uart2無modem控制,所以CTS、DSR直接返回有效 */
static unsigned int gprs_uart_get_mctrl ( struct uart_port * port )
{
return ( TIOCM_CTS | TIOCM_DSR | TIOCM_CAR ) ;
}
/* 設置串口modem控制 */
static void gprs_uart_set_mctrl ( struct uart_port * port , unsigned int mctrl )
{
}
/* 設置break信號 */
static void gprs_uart_break_ctl ( struct uart_port * port , int break_state )
{
unsigned long flags ;
unsigned int ucon ;
spin_lock_irqsave ( & port - > lock , flags ) ;
ucon = rd_regl ( port , S3C2410_UCON ) ;
if ( break_state )
ucon | = S3C2410_UCON_SBREAK ;
else
ucon & = ~ S3C2410_UCON_SBREAK ;
wr_regl ( port , S3C2410_UCON , ucon ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
}
/* 返回Rx FIFO已存多少數據 */
static int gprs_uart_rx_fifocnt ( unsigned long ufstat )
{
/* 若Rx FIFO已滿,返回FIFO的大小 */
if ( ufstat & S3C2410_UFSTAT_RXFULL )
return GPRS_UART_FIFO_SIZE ;
/* 若FIFO未滿,返回Rx FIFO已存了多少位元組數據 */
return ( ufstat & S3C2410_UFSTAT_RXMASK ) > > S3C2410_UFSTAT_RXSHIFT ;
}
# define S3C2410_UERSTAT_PARITY ( 0x1000 )
/* 串口接收中斷處理函數,獲取串口接收到的數據,並將這些數據遞交給行規則層 */
static irqreturn_t gprs_uart_rx_chars ( int irq , void * dev_id )
{
struct uart_port * port = dev_id ;
struct tty_struct * tty = port - > info - > tty ;
unsigned int ufcon , ch , flag , ufstat , uerstat ;
int max_count = 64 ;
/* 循環接收數據,最多一次中斷接收64位元組數據 */
while ( max_count - - > 0 )
{
ufcon = rd_regl ( port , S3C2410_UFCON ) ;
ufstat = rd_regl ( port , S3C2410_UFSTAT ) ;
/* 若Rx FIFO無數據了,跳出循環 */
if ( gprs_uart_rx_fifocnt ( ufstat ) = = 0 )
break ;
/* 讀取Rx error狀態寄存器 */
uerstat = rd_regl ( port , S3C2410_UERSTAT ) ;
/* 讀取已接受到的數據 */
ch = rd_regb ( port , S3C2410_URXH ) ;
/* insert the character into the buffer */
/* 先將tty標誌設為正常 */
flag = TTY_NORMAL ;
/* 遞增接收字元計數器 */
port - > icount . rx + + ;
/* 判斷是否存在Rx error
* if (unlikely(uerstat & S3C2410_UERSTAT_ANY))等同於
* if (uerstat & S3C2410_UERSTAT_ANY)
* 只是unlikely表示uerstat & S3C2410_UERSTAT_ANY的值為假的可能性大一些
* 另外還有一個likely(value)表示value的值為真的可能性更大一些
*/
if ( unlikely ( uerstat & S3C2410_UERSTAT_ANY ) )
{
/* 若break錯誤,遞增icount.brk計算器 */
if ( uerstat & S3C2410_UERSTAT_BREAK )
{
port - > icount . brk + + ;
if ( uart_handle_break ( port ) )
goto ignore_char ;
}
/* 若frame錯誤,遞增icount.frame計算器 */
if ( uerstat & S3C2410_UERSTAT_FRAME )
port - > icount . frame + + ;
/* 若overrun錯誤,遞增icount.overrun計算器 */
if ( uerstat & S3C2410_UERSTAT_OVERRUN )
port - > icount . overrun + + ;
/* 查看我們是否關心該Rx error
* port->read_status_mask保存著我們感興趣的Rx error status
*/
uerstat & = port - > read_status_mask ;
/* 若我們關心該Rx error,則將flag設置為對應的error flag */
if ( uerstat & S3C2410_UERSTAT_BREAK )
flag = TTY_BREAK ;
else if ( uerstat & S3C2410_UERSTAT_PARITY )
flag = TTY_PARITY ;
else if ( uerstat & ( S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN ) )
flag = TTY_FRAME ;
}
/* 處理sys字元 */
if ( uart_handle_sysrq_char ( port , ch ) )
goto ignore_char ;
/* 將接收到的字元插入到tty設備的flip緩衝 */
uart_insert_char ( port , uerstat , S3C2410_UERSTAT_OVERRUN , ch , flag ) ;
ignore_char :
continue ;
}
/* 刷新tty設備的flip緩衝,將接受到的數據傳給行規則層 */
tty_flip_buffer_push ( tty ) ;
return IRQ_HANDLED ;
}
/* 串口發送中斷處理函數,將用戶空間的數據(保存在環形緩衝xmit里)發送出去 */
static irqreturn_t gprs_uart_tx_chars ( int irq , void * dev_id )
{
struct uart_port * port = dev_id ;
struct circ_buf * xmit = & port - > info - > xmit ; /* 獲取環線緩衝 */
int count = 256 ;
/* 若設置了xChar字元 */
if ( port - > x_char )
{
/* 將該xChar發送出去 */
wr_regb ( port , S3C2410_UTXH , port - > x_char ) ;
/* 遞增發送計數 */
port - > icount . tx + + ;
/* 清除xChar */
port - > x_char = 0 ;
/* 退出中斷處理 */
goto out ;
}
/* 如果沒有更多的字元需要發送(環形緩衝為空),
* 或者uart Tx已停止,
* 則停止uart並退出中斷處理函數
*/
if ( uart_circ_empty ( xmit ) | | uart_tx_stopped ( port ) )
{
gprs_uart_stop_tx ( port ) ;
goto out ;
}
/* 循環發送數據,直到環形緩衝為空,最多一次中斷髮送256位元組數據 */
while ( ! uart_circ_empty ( xmit ) & & count - - > 0 )
{
/* 若Tx FIFO已滿,退出循環 */
if ( rd_regl ( port , S3C2410_UFSTAT ) & S3C2410_UFSTAT_TXFULL )
break ;
/* 將要發送的數據寫入Tx FIFO */
wr_regb ( port , S3C2410_UTXH , xmit - > buf [ xmit - > tail ] ) ;
/* 移向循環緩衝中下一要發送的數據 */
xmit - > tail = ( xmit - > tail + 1 ) & ( UART_XMIT_SIZE - 1 ) ;
port - > icount . tx + + ;
}
/* 如果環形緩衝區中剩餘的字元少於WAKEUP_CHARS,喚醒上層 */
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( port ) ;
/* 如果環形緩衝為空,則停止發送 */
if ( uart_circ_empty ( xmit ) )
gprs_uart_stop_tx ( port ) ;
out :
return IRQ_HANDLED ;
}
/* 啟動串口埠,在打開該驅動的設備文件時會調用該函數來申請串口中斷,並設置串口為可接受,也可發送 */
static int gprs_uart_startup ( struct uart_port * port )
{
unsigned long flags ;
int ret ;
const char * portname = to_platform_device ( port - > dev ) - > name ;
/* 設置串口為不可接受,也不可發送 */
rx_enabled ( port ) = 0 ;
tx_enabled ( port ) = 0 ;
spin_lock_irqsave ( & port - > lock , flags ) ;
/* 申請接收中斷 */
ret = request_irq ( RX_IRQ ( port ) , gprs_uart_rx_chars , 0 , portname , port ) ;
if ( ret ! = 0 )
{
printk ( KERN_ERR "cannot get irq %d\n" , RX_IRQ ( port ) ) ;
return ret ;
}
/* 設置串口為允許接收 */
rx_enabled ( port ) = 1 ;
/* 申請發送中斷 */
ret = request_irq ( TX_IRQ ( port ) , gprs_uart_tx_chars , 0 , portname , port ) ;
if ( ret )
{
printk ( KERN_ERR "cannot get irq %d\n" , TX_IRQ ( port ) ) ;
rx_enabled ( port ) = 0 ;
free_irq ( RX_IRQ ( port ) , port ) ;
goto err ;
}
/* 設置串口為允許發送 */
tx_enabled ( port ) = 1 ;
err :
spin_unlock_irqrestore ( & port - > lock , flags ) ;
return ret ;
}
/* 關閉串口,在關閉驅動的設備文件時會調用該函數,釋放串口中斷 */
static void gprs_uart_shutdown ( struct uart_port * port )
{
rx_enabled ( port ) = 0 ; /* 設置串口為不允許接收 */
free_irq ( RX_IRQ ( port ) , port ) ; /* 釋放接收中斷 */
tx_enabled ( port ) = 0 ; /* 設置串口為不允許發送 */
free_irq ( TX_IRQ ( port ) , port ) ; /* 釋放發送中斷 */
}
/* 設置串口參數 */
static void gprs_uart_set_termios ( struct uart_port * port ,
struct ktermios * termios ,
struct ktermios * old )
{
unsigned long flags ;
unsigned int baud , quot ;
unsigned int ulcon , ufcon = 0 ;
/* 不支持moden控制信號線
* HUPCL: 關閉時掛斷moden
* CMSPAR: mark or space (stick) parity
* CLOCAL: 忽略任何moden控制線
*/
termios - > c_cflag & = ~ ( HUPCL | CMSPAR ) ;
termios - > c_cflag | = CLOCAL ;
/* 獲取用戶設置的串口波特率,並計算分頻數(串口波特率除數quot) */
baud = uart_get_baud_rate ( port , termios , old , 0 , 115200 * 8 ) ;
if ( baud = = 38400 & & ( port - > flags & UPF_SPD_MASK ) = = UPF_SPD_CUST )
quot = port - > custom_divisor ;
else
quot = port - > uartclk / baud / 16 - 1 ;
/* 設置數據字長 */
switch ( termios - > c_cflag & CSIZE )
{
case CS5 :
ulcon = S3C2410_LCON_CS5 ;
break ;
case CS6 :
ulcon = S3C2410_LCON_CS6 ;
break ;
case CS7 :
ulcon = S3C2410_LCON_CS7 ;
break ;
case CS8 :
default :
ulcon = S3C2410_LCON_CS8 ;
break ;
}
/* 是否要求設置兩個停止位(CSTOPB) */
if ( termios - > c_cflag & CSTOPB )
ulcon | = S3C2410_LCON_STOPB ;
/* 是否使用奇偶檢驗 */
if ( termios - > c_cflag & PARENB )
{
if ( termios - > c_cflag & PARODD ) /* 奇校驗 */
ulcon | = S3C2410_LCON_PODD ;
else /* 偶校驗 */
ulcon | = S3C2410_LCON_PEVEN ;
}
else /* 無校驗 */
{
ulcon | = S3C2410_LCON_PNONE ;
}
if ( port - > fifosize > 1 )
ufcon | = S3C2410_UFCON_FIFOMODE | S3C2410_UFCON_RXTRIG8 ;
spin_lock_irqsave ( & port - > lock , flags ) ;
/* 設置FIFO控制寄存器、線控制寄存器和波特率除數寄存器 */
wr_regl ( port , S3C2410_UFCON , ufcon ) ;
wr_regl ( port , S3C2410_ULCON , ulcon ) ;
wr_regl ( port , S3C2410_UBRDIV , quot ) ;
/* 更新串口FIFO的超時時限 */
uart_update_timeout ( port , termios - > c_cflag , baud ) ;
/* 設置我們感興趣的Rx error */
port - > read_status_mask = S3C2410_UERSTAT_OVERRUN ;
if ( termios - > c_iflag & INPCK )
port - > read_status_mask | = S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY ;
/* 設置我們忽略的Rx error */
port - > ignore_status_mask = 0 ;
if ( termios - > c_iflag & IGNPAR )
port - > ignore_status_mask | = S3C2410_UERSTAT_OVERRUN ;
if ( termios - > c_iflag & IGNBRK & & termios - > c_iflag & IGNPAR )
port - > ignore_status_mask | = S3C2410_UERSTAT_FRAME ;
/* 若未設置CREAD(使用接收器),則忽略所有Rx error*/
if ( ( termios - > c_cflag & CREAD ) = = 0 )
port - > ignore_status_mask | = RXSTAT_DUMMY_READ ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
}
/* 獲取串口類型 */
static const char * gprs_uart_type ( struct uart_port * port )
{ /* 返回描述串口類型的字元串指針 */
return port - > type = = PORT_S3C2410 ? "gprs_uart:s3c2410_uart2" : NULL ;
}
/* 申請串口一些必要的資源,如IO埠/IO內存資源,必要時還可以重新映射串口埠 */
static int gprs_uart_request_port ( struct uart_port * port )
{
struct resource * res ;
const char * name = to_platform_device ( port - > dev ) - > name ;
/* request_mem_region請求分配IO內存,從開始port->mapbase,大小MAP_SIZE
* port->mapbase保存當前串口的寄存器基地址(物理)
* uart2: 0x50008000
*/
res = request_mem_region ( port - > mapbase , MAP_SIZE , name ) ;
if ( res = = NULL )
{
printk ( KERN_ERR "request_mem_region error: %p\n" , res ) ;
return - EBUSY ;
}
return 0 ;
}
/* 釋放串口已申請的IO埠/IO內存資源,必要時還需iounmap */
static void gprs_uart_release_port ( struct uart_port * port )
{
/* 釋放已分配IO內存 */
release_mem_region ( port - > mapbase , MAP_SIZE ) ;
}
/* 執行串口所需的自動配置 */
static void gprs_uart_config_port ( struct uart_port * port , int flags )
{
int retval ;
/* 請求串口 */
retval = gprs_uart_request_port ( port ) ;
/* 設置串口類型 */
if ( flags & UART_CONFIG_TYPE & & retval = = 0 )
port - > type = PORT_S3C2410 ;
}
/* The UART operations structure */
static struct uart_ops gprs_uart_ops = {
. start_tx = gprs_uart_start_tx , /* Start transmitting */
. stop_tx = gprs_uart_stop_tx , /* Stop transmission */
. stop_rx = gprs_uart_stop_rx , /* Stop reception */
. enable_ms = gprs_uart_enable_ms , /* Enable modem status signals */
. tx_empty = gprs_uart_tx_empty , /* Transmitter busy? */
. get_mctrl = gprs_uart_get_mctrl , /* Get modem control */
. set_mctrl = gprs_uart_set_mctrl , /* Set modem control */
. break_ctl = gprs_uart_break_ctl , /* Set break signal */
. startup = gprs_uart_startup , /* App opens GPRS_UART */
. shutdown = gprs_uart_shutdown , /* App closes GPRS_UART */
. set_termios = gprs_uart_set_termios , /* Set termios */
. type = gprs_uart_type , /* Get UART type */
. request_port = gprs_uart_request_port , /* Claim resources associated with a GPRS_UART port */
. release_port = gprs_uart_release_port , /* Release resources associated with a GPRS_UART port */
. config_port = gprs_uart_config_port , /* Configure when driver adds a GPRS_UART port */
} ;
/* Uart driver for GPRS_UART */
static struct uart_driver gprs_uart_driver = {
. owner = THIS_MODULE , /* Owner */
. driver_name = DEV_NAME , /* Driver name */
. dev_name = DEV_NAME , /* Device node name */
. major = GPRS_UART_MAJOR , /* Major number */
. minor = GPRS_UART_MINOR , /* Minor number start */
. nr = 1 , /* Number of UART ports */
} ;
/* Uart port for GPRS_UART port */
static struct uart_port gprs_uart_port = {
. irq = IRQ_S3CUART_RX2 , /* IRQ */
. fifosize = GPRS_UART_FIFO_SIZE , /* Size of the FIFO */
. iotype = UPIO_MEM , /* IO memory */
. flags = UPF_BOOT_AUTOCONF , /* UART port flag */
. ops = & gprs_uart_ops , /* UART operations */
. line = 0 , /* UART port number */
. lock = __SPIN_LOCK_UNLOCKED ( gprs_uart_port . lock ) ,
} ;
/* 初始化指定串口埠 */
static int gprs_uart_init_port ( struct uart_port * port , struct platform_device * platdev )
{
unsigned long flags ;
unsigned int gphcon ;
if ( platdev = = NULL )
return - ENODEV ;
port - > dev = & platdev - > dev ;
/* 設置串口波特率時鐘頻率 */
port - > uartclk = clk_get_rate ( clk_get ( & platdev - > dev , "pclk" ) ) ;
/* 設置串口的寄存器基地址(物理): 0x50008000 */
port - > mapbase = S3C2410_PA_UART2 ;
/* 設置當前串口的寄存器基地址(虛擬): 0xF5008000 */
port - > membase = S3C24XX_VA_UART + ( S3C2410_PA_UART2 - S3C24XX_PA_UART ) ;
spin_lock_irqsave ( & port - > lock , flags ) ;
wr_regl ( port , S3C2410_UCON , S3C2410_UCON_DEFAULT ) ;
wr_regl ( port , S3C2410_ULCON , S3C2410_LCON_CS8 | S3C2410_LCON_PNONE ) ;
wr_regl ( port , S3C2410_UFCON , S3C2410_UFCON_FIFOMODE
| S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_RESETBOTH ) ;
/* 將I/O port H的gph6和gph7設置為TXD2和RXD2 */
gphcon = readl ( S3C2410_GPHCON ) ;
gphcon & = ~ ( ( 0x5 ) < < 12 ) ;
writel ( gphcon , S3C2410_GPHCON ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
return 0 ;
}
/* Platform driver probe */
static int __init gprs_uart_probe ( struct platform_device * dev )
{
int ret ;
/* 初始化串口 */
ret = gprs_uart_init_port ( & gprs_uart_port , dev ) ;
if ( ret < 0 )
{
printk ( KERN_ERR "gprs_uart_probe: gprs_uart_init_port error: %d\n" , ret ) ;
return ret ;
}
/* 添加串口 */
ret = uart_add_one_port ( & gprs_uart_driver , & gprs_uart_port ) ;
if ( ret < 0 )
{
printk ( KERN_ERR "gprs_uart_probe: uart_add_one_port error: %d\n" , ret ) ;
return ret ;
}
/* 將串口uart_port結構體保存在platform_device->dev->driver_data中 */
platform_set_drvdata ( dev , & gprs_uart_port ) ;
return 0 ;
}
/* Called when the platform driver is unregistered */
static int gprs_uart_remove ( struct platform_device * dev )
{
platform_set_drvdata ( dev , NULL ) ;
/* 移除串口 */
uart_remove_one_port ( & gprs_uart_driver , & gprs_uart_port ) ;
return 0 ;
}
/* Suspend power management event */
static int gprs_uart_suspend ( struct platform_device * dev , pm_message_t state )
{
uart_suspend_port ( & gprs_uart_driver , & gprs_uart_port ) ;
return 0 ;
}
/* Resume after a previous suspend */
static int gprs_uart_resume ( struct platform_device * dev )
{
uart_resume_port ( & gprs_uart_driver , & gprs_uart_port ) ;
return 0 ;
}
/* Platform driver for GPRS_UART */
static struct platform_driver gprs_plat_driver = {
. probe = gprs_uart_probe , /* Probe method */
. remove = __exit_p ( gprs_uart_remove ) , /* Detach method */
. suspend = gprs_uart_suspend , /* Power suspend */
. resume = gprs_uart_resume , /* Resume after a suspend */
. driver = {
. owner = THIS_MODULE ,
. name = DEV_NAME , /* Driver name */
} ,
} ;
/* Platform device for GPRS_UART */
struct platform_device * gprs_plat_device ;
static int __init gprs_init_module ( void )
{
int retval ;
/* Register uart_driver for GPRS_UART */
retval = uart_register_driver ( & gprs_uart_driver ) ;
if ( 0 ! = retval )
{
printk ( KERN_ERR "gprs_init_module: can't register the GPRS_UART driver %d\n" , retval ) ;
return retval ;
}
/* Register platform device for GPRS_UART. Usually called
during architecture-specific setup */
gprs_plat_device = platform_device_register_simple ( DEV_NAME , 0 , NULL , 0 ) ;
if ( IS_ERR ( gprs_plat_device ) )
{
retval = PTR_ERR ( gprs_plat_device ) ;
printk ( KERN_ERR "gprs_init_module: can't register platform device %d\n" , retval ) ;
goto fail_reg_plat_dev ;
}
/* Announce a matching driver for the platform
devices registered above */
retval = platform_driver_register ( & gprs_plat_driver ) ;
if ( 0 ! = retval )
{
printk ( KERN_ERR "gprs_init_module: can't register platform driver %d\n" , retval ) ;
goto fail_reg_plat_drv ;
}
return 0 ; /* succeed */
fail_reg_plat_drv :
platform_device_unregister ( gprs_plat_device ) ;
fail_reg_plat_dev :
uart_unregister_driver ( & gprs_uart_driver ) ;
return retval ;
}
static void __exit gprs_exit_module ( void )
{
/* The order of unregistration is important. Unregistering the
UART driver before the platform driver will crash the system */
/* Unregister the platform driver */
platform_driver_unregister ( & gprs_plat_driver ) ;
/* Unregister the platform devices */
platform_device_unregister ( gprs_plat_device ) ;
/* Unregister the GPRS_UART driver */
uart_unregister_driver ( & gprs_uart_driver ) ;
}
module_init ( gprs_init_module ) ;
module_exit ( gprs_exit_module ) ;
MODULE_AUTHOR ( "lingd" ) ;
MODULE_LICENSE ( "Dual BSD/GPL" ) ;