打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Linux串口(serial、uart)驅動程序設計|精彩博文
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" ) ;

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
tty初探
修正串口控制台无法输入的BUG
Zigbee之旅(十):综合小实验——基于CC2430的温度监测系统(转)
s3c2440 串口1驱动程序
bootloader功能介绍/时钟初始化设置/串口工作原理/内存工作原理/NandFlash工作原理
C8051F330串口UART0示例程序
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服