亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

linux串口驅動分析

系統 3434 0
linux串口驅動分析



  • 硬件資源及描寫敘述
??? ??? s3c2440A 通用異步接收器和發送器(UART)提供了三個獨立的異步串行 I/O(SIO)port,每一個port都能夠在中斷模式或 DMA 模式下操作。UART 使用系統時鐘能夠支持最高 115.2Kbps 的波特率。每一個 UART 通道對于接收器和發送器包含了 2 個 64 位的 FIFO。

寄存器 名稱 地址 在linux中的描寫敘述 ( 2410 和 2440 處理器對內存地址映射關系同樣 )
UART 線性控制寄存器(ULCONn) ULCON0?
ULCON1?
ULCON2?
0x50000000
0x50004000
0x50008000
linux-2.6.31/arch/arm/plat-s3c/include/plat/regs-serial.h

#define S3C2410_PA_UART0 ??????

? ? (S3C24XX_PA_UART)

#define S3C2410_PA_UART1 ?????

? ? (S3C24XX_PA_UART + 0x4000 )

#define S3C2410_PA_UART2 ?????

? ? (S3C24XX_PA_UART + 0x8000 )

#define S3C2443_PA_UART3?????

? ? (S3C24XX_PA_UART + 0xC000 )

linux-2.6.31/arch/arm/plat-s3c/include/plat/map.h

#define S3C24XX_VA_UART??? ??

? ? S3C_VA_UART//靜態映射后的虛擬地址

#define S3C2410_PA_UART??? ??

? ? ( 0x50000000 )//物理地址

#define S3C24XX_SZ_UART??? ?? SZ_1M
#define S3C_UART_OFFSET??? ?? ( 0x4000 )
UART 控制寄存器(UCONn) UCON0?
UCON1
UCON2
0x50000004
0x50004004
0x50008004
#define S3C2410_UCON??? ? (0x04)
UART FIFO 控制寄存器(UFCONn) UFCON0
UFCON1
UFCON2
0x50000008
0x50004008
0x50008008
#define S3C2410_UFCON??? ? (0x08)
UART MODEM 控制寄存器(UMCONn) UMCON0
UMCON1
0x5000000C
0x5000400C
#define S3C2410_UMCON??? ? (0x0C)
UART 接收發送狀態寄存器(UTRSTATn) UTRSTAT0
UTRSTAT1
UTRSTAT2
0x50000010
0x50004010
0x50008010
#define S3C2410_UTRSTAT??? ? (0x10)
UART 錯誤狀態寄存器(UERSTATn) UERSTAT0
UERSTAT1
UERSTAT2
0x50000014
0x50004014
0x50008014
#define S3C2410_UERSTAT??? ? (0x14)
UART FIFO 狀態寄存器(UFSTATn) UFSTAT0
UFSTAT1
UFSTAT2
0x50000018
0x50004018
0x50008018
#define S3C2410_UFSTAT??? ? (0x18)
UART MODEM 狀態寄存器(UMSTATn) UFSTAT0
UFSTAT1
0x5000001C
0x5000401C
#define S3C2410_UMSTAT??? ? (0x1C)
UART 發送緩存寄存器(UTXHn) UTXH0

UTXH1

UTXH2
0x50000020(L)
0x50000023(B)
0x50004020(L)
0x50004023(B)
0x50008020(L)
0x50008023(B)
#define S3C2410_UTXH??? ? (0x20)
UART 接收緩存寄存器(URXHn) URXH0

URXH1

URXH2
0x50000024(L)
0x50000027(B)
0x50004024(L)
0x50004027(B)
0x50008024(L)
0x50008027(B)
#define S3C2410_URXH??? ? (0x24)
UART 波特率除數寄存器(UBRDIVn) UBRDIV0
UBRDIV1
UBRDIV2
0x50000028
0x50004028
0x50008028
#define S3C2410_UBRDIV??? ? (0x28)

ULCON 描寫敘述 在linux中的描寫敘述 linux-2.6.31/arch/arm/plat-s3c/include/plat/regs-serial.h
Reserved [7] ? ?
Infrared Mode [6] 決定是否使用紅外模式
0 =正常模式操作
1 = 紅外接收發送模式
#define S3C2410_LCON_IRM????????? (1<<6)
Parity Mode [5:3] 在UART發送接收操作中定義奇偶碼的生成和檢
驗類型
0xx = No parity
100 = Odd parity
101 = Even parity
110 = Parity forced/checked as 1
111 = Parity forced/checked as 0
#define S3C2410_LCON_PNONE??? ? (0x0)
#define S3C2410_LCON_PEVEN??? ? (0x5 << 3)
#define S3C2410_LCON_PODD??? ? (0x4 << 3)
#define S3C2410_LCON_PMASK??? ? (0x7 << 3)
Number of Stop Bit [2] 定義度搜按個停止位用于幀末信號
0 =每幀一個停止位
1 =每幀兩個停止位
#define S3C2410_LCON_STOPB??? ? (1<<2)
Word Length [1:0] 指出發送接收每幀的數據位數
00 = 5-bits 01 = 6-bits
10 = 7-bits 11 = 8-bits
#define S3C2410_LCON_CS5??? ? (0x0)
#define S3C2410_LCON_CS6??? ? (0x1)
#define S3C2410_LCON_CS7??? ? (0x2)
#define S3C2410_LCON_CS8??? ? (0x3)
#define S3C2410_LCON_CSMASK??? ? (0x3)
?
UFCON 描寫敘述 在linux中的描寫敘述

Tx FIFO Trigger Level
[7:6] 決定發送FIFO的觸發等級
00 = Empty 01 = 16-byte
10 = 32-byte 11 = 48-byte
#define S3C2440_UFCON_TXTRIG0??? ? (0<<6)
#define S3C2440_UFCON_TXTRIG16??? ? (1<<6)
#define S3C2440_UFCON_TXTRIG32??? ? (2<<6)
#define S3C2440_UFCON_TXTRIG48??? ? (3<<6)

Rx FIFO Trigger Level
[5:4] 決定接收FIFO的觸發等級
00 = 1-byte 01 = 8-byte
10 = 16-byte 11 = 32-byte
#define S3C2440_UFCON_RXTRIG1??? ? (0<<4)
#define S3C2440_UFCON_RXTRIG8??? ? (1<<4)
#define S3C2440_UFCON_RXTRIG16??? ? (2<<4)
#define S3C2440_UFCON_RXTRIG32??? ? (3<<4)
Reserved [3] ? ?
Tx FIFO Reset [2] 在重置FIFO后自己主動清除
0 = Normal
1= Tx FIFO reset
#define S3C2410_UFCON_RESETTX??? ? (1<<2)
Rx FIFO Reset [1] 在重置FIFO后自己主動清除
0 = Normal
1= Rx FIFO reset
#define S3C2410_UFCON_RESETRX??? ? (1<<1)
#define S3C2410_UFCON_RESETBOTH??? ? (3<<1)
FIFO Enable [0] 0 = Disable
1 = Enable
?
默認設置為?
?#define S3C2410_UFCON_DEFAULT??? ? (S3C2410_UFCON_FIFOMODE | \
??? ??? ??? ??? ?? S3C2410_UFCON_TXTRIG0? | \
??? ??? ??? ??? ?? S3C2410_UFCON_RXTRIG8 )

使能FIFO
Tx FIFO為空時觸發中斷
Rx FIFO中包括8個字節時觸發中斷
?
UFSTAT 描寫敘述 在linux中的表示
Reserved [15] ? ?
Tx FIFO Full
[14] 僅僅要在發送操作中發送FIFO滿,則自己主動置 1。
0 = 0-byte ≤ Tx FIFO data ≤ 63-byte
1 = Full
#define S3C2440_UFSTAT_TXFULL??? ? (1<<14)
Tx FIFO Count [13:8] 發送FIFO中的數據數量 #define S3C2440_UFSTAT_TXSHIFT??? ? (8)
Reserved [7] ? ?
Rx FIFO Full [6] 僅僅要在接收操作中接收FIFO滿,則自己主動置 1。
0 = 0-byte ≤ Rx FIFO data ≤ 63-byte
1 = Full
#define S3C2440_UFSTAT_RXFULL??? ? (1<<6)
Rx FIFO Count [5:0] 接收FIFO中的數據數量 #define S3C2440_UFSTAT_RXSHIFT??? ? (0)
? ? ? ?



  • uart驅動程序概述
??? ??? linux的uart驅動程序建立在tty驅動程序之上(串口也是一個終端),程序源碼主要在drivers/serial/文件夾下。當中 serial_core.c實現了uart設備的通用tty驅動層,當中定義了一組通用接口,包含3個結構體:uart_driver, uart_port,uart_ops( include/serial_core.h )。因此,實現一個平臺的uart驅動程序僅僅要實現這3個結構體就可以。


serial_core(定義): samsumg.c (實現):??????????? ???????????????????????????????????????????????????????
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 s3c24xx_uart_drv = {
??? .owner??? ??? = THIS_MODULE,
??? .dev_name??? = "s3c2410_serial",//設備名稱,這個名稱必須和
??????? //根文件系統中/etc/inittab文件里的串口名稱一致

??? .nr??? ??? = CONFIG_SERIAL_SAMSUNG_UARTS,
??? .cons??? ??? = S3C24XX_SERIAL_CONSOLE,
??? .driver_name??? = S3C24XX_SERIAL_NAME,//驅動名稱
??? .major??? ??? = S3C24XX_SERIAL_MAJOR,
??? .minor??? ??? = S3C24XX_SERIAL_MINOR,
};
??? ???
??? ??? uart_driver封裝了tty_driver,使底層uart驅動不用關心ttr_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()的操作。

serial_core(定義): samsumg.h? samsumg.c(實現):
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);
??? unsigned int??? ??? irq;??? ??? ??? /* irq number */
??? unsigned int??? ??? uartclk;??? ??? /* base uart clock */
??? unsigned int??? ??? fifosize;??? ??? /* tx fifo size */
??? unsigned char??? ??? x_char;??? ??? ??? /* xon/xoff char */
??? unsigned char??? ??? regshift;??? ??? /* reg offset shift */
??? unsigned char??? ??? iotype;??? ??? ??? /* io access style */
??? unsigned char??? ??? unused1;

#define UPIO_PORT??? ??? (0)
#define UPIO_HUB6??? ??? (1)
#define UPIO_MEM??? ??? (2)
#define UPIO_MEM32??? ??? (3)
#define UPIO_AU??? ??? ??? (4)??? ??? ??? /* Au1x00 type IO */
#define UPIO_TSI??? ??? (5)??? ??? ??? /* Tsi108/109 type IO */
#define UPIO_DWAPB??? ??? (6)??? ??? ??? /* DesignWare APB UART */
#define UPIO_RM9000??? ??? (7)??? ??? ??? /* RM9000 type IO */

??? unsigned int??? ??? read_status_mask;??? /* driver specific */
??? unsigned int??? ??? ignore_status_mask;??? /* driver specific */
??? struct uart_info??? *info;??? ??? ??? /* pointer to parent info */
??? struct uart_icount??? icount;??? ??? ??? /* statistics */

??? struct console??? ??? *cons;??? ??? ??? /* struct console, if any */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
??? unsigned long??? ??? sysrq;??? ??? ??? /* sysrq timeout */
#endif

??? upf_t??? ??? ??? flags;

#define UPF_FOURPORT??? ??? ((__force upf_t) (1 << 1))
#define UPF_SAK??? ??? ??? ((__force upf_t) (1 << 2))
#define UPF_SPD_MASK??? ??? ((__force upf_t) (0x1030))
#define UPF_SPD_HI??? ??? ((__force upf_t) (0x0010))
#define UPF_SPD_VHI??? ??? ((__force upf_t) (0x0020))
#define UPF_SPD_CUST??? ??? ((__force upf_t) (0x0030))
#define UPF_SPD_SHI??? ??? ((__force upf_t) (0x1000))
#define UPF_SPD_WARP??? ??? ((__force upf_t) (0x1010))
#define UPF_SKIP_TEST??? ??? ((__force upf_t) (1 << 6))
#define UPF_AUTO_IRQ??? ??? ((__force upf_t) (1 << 7))
#define UPF_HARDPPS_CD??? ??? ((__force upf_t) (1 << 11))
#define UPF_LOW_LATENCY??? ??? ((__force upf_t) (1 << 13))
#define UPF_BUGGY_UART??? ??? ((__force upf_t) (1 << 14))
#define UPF_NO_TXEN_TEST??? ((__force upf_t) (1 << 15))
#define UPF_MAGIC_MULTIPLIER??? ((__force upf_t) (1 << 16))
#define UPF_CONS_FLOW??? ??? ((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ??? ??? ((__force upf_t) (1 << 24))
/* The exact UART type is known and should not be probed.? */
#define UPF_FIXED_TYPE??? ??? ((__force upf_t) (1 << 27))
#define UPF_BOOT_AUTOCONF??? ((__force upf_t) (1 << 28))
#define UPF_FIXED_PORT??? ??? ((__force upf_t) (1 << 29))
#define UPF_DEAD??? ??? ((__force upf_t) (1 << 30))
#define UPF_IOREMAP??? ??? ((__force upf_t) (1 << 31))

#define UPF_CHANGE_MASK??? ??? ((__force upf_t) (0x17fff))
#define UPF_USR_MASK??? ??? ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))

??? unsigned int??? ??? mctrl;??? ??? ??? /* current modem ctrl settings */
??? unsigned int??? ??? timeout;??? ??? /* character-based timeout */
??? unsigned int??? ??? type;??? ??? ??? /* port type */
??? const struct uart_ops??? *ops;
??? 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??? ??? unused[2];
??? void??? ??? ??? *private_data;??? ??? /* generic platform data pointer */
};
struct s3c24xx_uart_port { // 封裝了uart_port
??? unsigned char??? ??? ??? rx_claimed;
??? unsigned char??? ??? ??? tx_claimed;
??? unsigned int??? ??? ??? pm_level;
??? unsigned long??? ??? ??? baudclk_rate;

??? unsigned int??? ??? ??? rx_irq;
??? unsigned int??? ??? ??? tx_irq;

??? struct s3c24xx_uart_info??? *info;
??? struct s3c24xx_uart_clksrc??? *clksrc;
??? struct clk??? ??? ??? *clk;
??? struct clk??? ??? ??? *baudclk;
??? struct uart_port??? ??? port;

#ifdef CONFIG_CPU_FREQ
??? struct notifier_block??? ??? freq_transition;
#endif
};

static struct s3c24xx_uart_port s3c24xx_serial_ports[ CONFIG_SERIAL_SAMSUNG_UARTS ] = { //3 uarts default
??? [0] = {
??? ??? .port = {
??? ??? ??? .lock??? ??? = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
??? ??? ??? .iotype??? ??? = UPIO_MEM,
??? ??? ??? .irq??? ??? = IRQ_S3CUART_RX0,
??? ??? ??? .uartclk??? = 0,
??? ??? ??? .fifosize??? = 16,
??? ??? ??? .ops??? ??? = &s3c24xx_serial_ops,
??? ??? ??? .flags??? ??? = UPF_BOOT_AUTOCONF,
??? ??? ??? .line??? ??? = 0,
??? ??? }
??? },
??? [1] = {
??? ??? .port = {
??? ??? ??? .lock??? ??? = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
??? ??? ??? .iotype??? ??? = UPIO_MEM,
??? ??? ??? .irq??? ??? = IRQ_S3CUART_RX1,
??? ??? ??? .uartclk??? = 0,
??? ??? ??? .fifosize??? = 16,
??? ??? ??? .ops??? ??? = &s3c24xx_serial_ops,
??? ??? ??? .flags??? ??? = UPF_BOOT_AUTOCONF,
??? ??? ??? .line??? ??? = 1,
??? ??? }
??? },
#if CONFIG_SERIAL_SAMSUNG_UARTS > 2

??? [2] = {
??? ??? .port = {
??? ??? ??? .lock??? ??? = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
??? ??? ??? .iotype??? ??? = UPIO_MEM,
??? ??? ??? .irq??? ??? = IRQ_S3CUART_RX2,
??? ??? ??? .uartclk??? = 0,
??? ??? ??? .fifosize??? = 16,
??? ??? ??? .ops??? ??? = &s3c24xx_serial_ops,
??? ??? ??? .flags??? ??? = UPF_BOOT_AUTOCONF,
??? ??? ??? .line??? ??? = 2,
??? ??? }
??? },
#endif
#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
??? [3] = {
??? ??? .port = {
??? ??? ??? .lock??? ??? = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
??? ??? ??? .iotype??? ??? = UPIO_MEM,
??? ??? ??? .irq??? ??? = IRQ_S3CUART_RX3,
??? ??? ??? .uartclk??? = 0,
??? ??? ??? .fifosize??? = 16,
??? ??? ??? .ops??? ??? = &s3c24xx_serial_ops,
??? ??? ??? .flags??? ??? = UPF_BOOT_AUTOCONF,
??? ??? ??? .line??? ??? = 3,
??? ??? }
??? }
#endif
};
??? ???
??? ??? uart_port用于描寫敘述一個UARTport(直接相應于一個串口)的I/Oport或I/O內存地址、FIFO大小、port類型等信息。串口核心層提供例如以下函數來加入1個port:

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

serial_core.h(定義): samsumg.c (實現):??? ???????????????????????????????????????????????????????????????????????????????????????
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??? ??? (*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 *);
??? void??? ??? (*pm)(struct uart_port *, unsigned int state,
??? ??? ??? ????? unsigned int oldstate);
??? int??? ??? (*set_wake)(struct uart_port *, unsigned int state);

??? /*
??? ?* 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
??? void??? (*poll_put_char)(struct uart_port *, unsigned char);
??? int??? ??? (*poll_get_char)(struct uart_port *);
#endif
};
static struct uart_ops s3c24xx_serial_ops = {
??? .pm??? ??? = s3c24xx_serial_pm,
??? .tx_empty??? = s3c24xx_serial_tx_empty,
??? .get_mctrl??? = s3c24xx_serial_get_mctrl,
??? .set_mctrl??? = s3c24xx_serial_set_mctrl,
??? .stop_tx??? = s3c24xx_serial_stop_tx,
??? .start_tx??? = s3c24xx_serial_start_tx,
??? .stop_rx??? = s3c24xx_serial_stop_rx,
??? .enable_ms??? = s3c24xx_serial_enable_ms,
??? .break_ctl??? = s3c24xx_serial_break_ctl,
??? .startup??? = s3c24xx_serial_startup,
??? .shutdown??? = s3c24xx_serial_shutdown,
??? .set_termios??? = s3c24xx_serial_set_termios,
??? .type??? ??? = s3c24xx_serial_type,
??? .release_port??? = s3c24xx_serial_release_port,
??? .request_port??? = s3c24xx_serial_request_port,
??? .config_port??? = s3c24xx_serial_config_port,
??? .verify_port??? = s3c24xx_serial_verify_port,
};
在使用串口核心層這個通用串口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驅動的主體工作。
  • 串口驅動初始化過程
??? ??? 在S3C2410 串口驅動的模塊載入函數中會調用uart_register_driver()注冊s3c24xx_uart_drv這個uart_driver,同一時候經過
s3c2410_serial_init()→s3c24xx_serial_init()→ platform_driver_register() 的調用導致s3c24xx_serial_probe()被運行,而s3c24xx_serial_probe()函數中會調用 s3c24xx_serial_init_port()初始化UART端口并調用uart_add_one_port()加入端口。
platform_device_driver 參考:
Linux Platform Device and Driver ?? Linux driver model ----- platform

回過頭來看s3c24xx_uart_info結構體(s3c24xx_uart_port的成員),是一些針對s3c2440 uart 的信息,在/drivers/serial/s3c2440.c中:

static struct s3c24xx_uart_info s3c2440_uart_inf = {
??? .name??? ??? = "Samsung S3C2440 UART",
??? .type??? ??? = PORT_S3C2440,
??? .fifosize??? = 64,
??? .rx_fifomask??? = S3C2440_UFSTAT_RXMASK,
??? .rx_fifoshift??? = S3C2440_UFSTAT_RXSHIFT,
??? .rx_fifofull??? = S3C2440_UFSTAT_RXFULL,
??? .tx_fifofull??? = S3C2440_UFSTAT_TXFULL,
??? .tx_fifomask??? = S3C2440_UFSTAT_TXMASK,
??? .tx_fifoshift??? = S3C2440_UFSTAT_TXSHIFT,
??? .get_clksrc??? = s3c2440_serial_getsource,
??? .set_clksrc??? = s3c2440_serial_setsource,
??? .reset_port??? = s3c2440_serial_resetport,
};


  • 串口操作函數,uart_ops接口函數
S3C2410串口驅動uart_ops結構體的startup ()成員函數s3c24xx_serial_startup()用于啟動port,申請port的發送、接收中斷,使能port的發送和接收。

static int s3c24xx_serial_startup (struct uart_port *port)
{
??? struct s3c24xx_uart_port *ourport = to_ourport(port);
??? int ret;

??? dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
??? ??? port->mapbase, port->membase);

??? rx_enabled(port) = 1;//接收使能

??? ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars , 0,
??? ??? ??? ? s3c24xx_serial_portname(port), ourport); //申請接收中斷, s3c24xx_serial_rx_chars是中斷處理函數

??? if (ret != 0) {
??? ??? printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
??? ??? return ret;
??? }

??? ourport->rx_claimed = 1;

??? dbg("requesting tx irq...\n");

??? tx_enabled(port) = 1;//發送使能

??? ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
??? ??? ??? ? s3c24xx_serial_portname(port), ourport); //申請發送中斷

??? if (ret) {
??? ??? printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
??? ??? goto err;
??? }

??? ourport->tx_claimed = 1;

??? dbg("s3c24xx_serial_startup ok\n");

??? /* the port reset code should have done the correct
??? ?* register setup for the port controls */

??? return ret;

?err:
??? s3c24xx_serial_shutdown(port);
??? return ret;
}

s3c24xx_serial_startup()的“反函數”為s3c24xx_serial_shutdown(),其釋放中斷,禁止發送和接收。

static void s3c24xx_serial_shutdown(struct uart_port *port)
{
??? struct s3c24xx_uart_port *ourport = to_ourport(port);

??? if (ourport->tx_claimed) {
??? ??? free_irq(ourport->tx_irq, ourport);
??? ??? tx_enabled(port) = 0;
??? ??? ourport->tx_claimed = 0;
??? }

??? if (ourport->rx_claimed) {
??? ??? free_irq(ourport->rx_irq, ourport);
??? ??? ourport->rx_claimed = 0;
??? ??? rx_enabled(port) = 0;
??? }
}

tx_empty()成員函數s3c24xx_serial_tx_empty()用于推斷發送緩沖區是否為空,當使能FIFO模式的時候,推斷UFSTATn寄存器,否則推斷UTRSTATn寄存器的對應位。

static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
{
??? struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
??? unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);
??? unsigned long ufcon = rd_regl(port, S3C2410_UFCON);

??? if (ufcon & S3C2410_UFCON_FIFOMODE) {
??? ??? if ((ufstat & info->tx_fifomask) != 0 ||
??? ??? ??? (ufstat & info->tx_fifofull))
??? ??? ??? return 0;

??? ??? return 1;
??? }

??? return s3c24xx_serial_txempty_nofifo(port);
}

static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
{
??? return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE);
}
在samsung.h中定義了例如以下操作寄存器的宏:
/* register access controls */

#define portaddr(port, reg) ((port)->membase + (reg))

#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))? //read regester long

#define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg))
#define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg))

start_tx ()成員函數s3c24xx_serial_start_tx()用于啟動發送,而stop_rx()成員函數 s3c24xx_serial_stop_tx()用于停止發送。

static void s3c24xx_serial_start_tx(struct uart_port *port)
{
??? struct s3c24xx_uart_port *ourport = to_ourport(port);

??? if (!tx_enabled(port)) {
??? ??? if (port->flags & UPF_CONS_FLOW)
??? ??? ??? s3c24xx_serial_rx_disable(port);

??? ??? enable_irq(ourport->tx_irq);
??? ??? tx_enabled(port) = 1;
??? }
}
static void s3c24xx_serial_stop_tx(struct uart_port *port)
{
??? struct s3c24xx_uart_port *ourport = to_ourport(port);

??? if (tx_enabled(port)) {
??? ??? disable_irq_nosync(ourport->tx_irq);
??? ??? tx_enabled(port) = 0;
??? ??? if (port->flags & UPF_CONS_FLOW)
??? ??? ??? s3c24xx_serial_rx_enable(port);
??? }
}

??? ??? S3C2410 串口驅動uart_ops結構體的set_termios()成員函數用于改變端口的參數設置,包含波特率、字長、停止位、奇偶校驗等,它會依據傳遞給它的port、termios參數成員的值設置S3C2410 UART的ULCONn、UCONn、UMCONn等寄存器。

static void s3c24xx_serial_set_termios(struct uart_port *port,
??? ??? ??? ??? ?????? struct ktermios *termios,
??? ??? ??? ??? ?????? struct ktermios *old)
{
??? struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
??? struct s3c24xx_uart_port *ourport = to_ourport(port);
??? struct s3c24xx_uart_clksrc *clksrc = NULL;
??? struct clk *clk = NULL;
??? unsigned long flags;
??? unsigned int baud, quot;
??? unsigned int ulcon;
??? unsigned int umcon;
??? unsigned int udivslot = 0;

??? /*
??? ?* We don't support modem control lines.
??? ?*/
??? termios->c_cflag &= ~(HUPCL | CMSPAR);
??? termios->c_cflag |= CLOCAL;

??? /*
??? ?* Ask the core to calculate the divisor for us.請求內核計算分頻以便產生相應的波特率
??? ?*/

??? 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 = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);

??? /* check to see if we need? to change clock source 檢查以確定是否須要改變時鐘源 */

??? if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
??? ??? dbg("selecting clock %p\n", clk);
??? ??? s3c24xx_serial_setsource(port, clksrc);

??? ??? if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
??? ??? ??? clk_disable(ourport->baudclk);
??? ??? ??? ourport->baudclk? = NULL;
??? ??? }

??? ??? clk_enable(clk);

??? ??? ourport->clksrc = clksrc;
??? ??? ourport->baudclk = clk;
??? ??? ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
??? }

??? if (ourport->info->has_divslot) {
??? ??? unsigned int div = ourport->baudclk_rate / baud;

??? ??? udivslot = udivslot_table[div & 15];
??? ??? dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
??? }
??? /* 設置字長 */
??? switch (termios->c_cflag & CSIZE) {
??? case CS5:
??? ??? dbg("config: 5bits/char\n");
??? ??? ulcon = S3C2410_LCON_CS5;
??? ??? break;
??? case CS6:
??? ??? dbg("config: 6bits/char\n");
??? ??? ulcon = S3C2410_LCON_CS6;
??? ??? break;
??? case CS7:
??? ??? dbg("config: 7bits/char\n");
??? ??? ulcon = S3C2410_LCON_CS7;
??? ??? break;
??? case CS8:
??? default:
??? ??? dbg("config: 8bits/char\n");
??? ??? ulcon = S3C2410_LCON_CS8;
??? ??? break;
??? }

??? /* preserve original lcon IR settings */
??? ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);

??? if (termios->c_cflag & CSTOPB)
??? ??? ulcon |= S3C2410_LCON_STOPB;

??? umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;

??? if (termios->c_cflag & PARENB) {
??? ??? if (termios->c_cflag & PARODD)
??? ??? ??? ulcon |= S3C2410_LCON_PODD;
??? ??? else
??? ??? ??? ulcon |= S3C2410_LCON_PEVEN;
??? } else {
??? ??? ulcon |= S3C2410_LCON_PNONE;
??? }

??? spin_lock_irqsave(&port->lock, flags);

??? dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n",
??? ??? ulcon, quot, udivslot);

??? wr_regl(port, S3C2410_ULCON, ulcon);
??? wr_regl(port, S3C2410_UBRDIV, quot);
??? wr_regl(port, S3C2410_UMCON, umcon);

??? if (ourport->info->has_divslot)
??? ??? wr_regl(port, S3C2443_DIVSLOT, udivslot);

??? dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
??? ??? rd_regl(port, S3C2410_ULCON),
??? ??? rd_regl(port, S3C2410_UCON),
??? ??? rd_regl(port, S3C2410_UFCON));

??? /*
??? ?* Update the per-port timeout.
??? ?*/
??? uart_update_timeout(port, termios->c_cflag, baud);

??? /*
??? ?* Which character status flags are we interested in?
??? ?*/
??? port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
??? if (termios->c_iflag & INPCK)
??? ??? port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;

??? /*
??? ?* Which character status flags should we ignore?
??? ?*/
??? 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;

??? /*
??? ?* Ignore all characters if CREAD is not set.
??? ?*/
??? if ((termios->c_cflag & CREAD) == 0)
??? ??? port->ignore_status_mask |= RXSTAT_DUMMY_READ;

??? spin_unlock_irqrestore(&port->lock, flags);
}


  • 接收和發送中斷處理函數
??? ??? 在S3C2410 串口驅動中,與數據收發關系最密切的函數不是上述uart_ops成員函數,而是s3c24xx_serial_startup()為發送和接收中斷注冊的中斷處理函數s3c24xx_serial_rx_chars()和s3c24xx_serial_tx_chars()。
??? ??? s3c24xx_serial_rx_chars ()讀取URXHn寄存器以獲得接收到的字符,并調用 uart_insert_char() 將該字符加入了tty設備的flip緩沖區中,當接收到64個字符或者不再能接受到字符后,調用tty_flip_buffer_push()函數向上層“推”tty設備的flip緩沖。
??? ?? s3c24xx_serial_tx_chars()讀取uart_info中環形緩沖區中的字符,寫入調用UTXHn寄存器。

#define S3C2410_UERSTAT_PARITY (0x1000)

static irqreturn_t s3c24xx_serial_rx_chars (int irq, void *dev_id)
{
??? struct s3c24xx_uart_port *ourport = dev_id;
??? struct uart_port *port = &ourport->port;
??? struct tty_struct *tty = port->info->port.tty;
??? unsigned int ufcon, ch, flag, ufstat, uerstat;
??? int max_count = 64;

??? while (max_count-- > 0) {
??? ??? ufcon = rd_regl(port, S3C2410_UFCON);
??? ??? ufstat = rd_regl(port, S3C2410_UFSTAT);

??? ??? if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
??? ??? ??? break;

??? ??? uerstat = rd_regl(port, S3C2410_UERSTAT);
??? ??? ch = rd_regb(port, S3C2410_URXH);

??? ??? if (port->flags & UPF_CONS_FLOW) {
??? ??? ??? int txe = s3c24xx_serial_txempty_nofifo(port);

??? ??? ??? if (rx_enabled(port)) {
??? ??? ??? ??? if (!txe) {
??? ??? ??? ??? ??? rx_enabled(port) = 0;
??? ??? ??? ??? ??? continue;
??? ??? ??? ??? }
??? ??? ??? } else {
??? ??? ??? ??? if (txe) {
??? ??? ??? ??? ??? ufcon |= S3C2410_UFCON_RESETRX;
??? ??? ??? ??? ??? wr_regl(port, S3C2410_UFCON, ufcon);
??? ??? ??? ??? ??? rx_enabled(port) = 1;
??? ??? ??? ??? ??? goto out;
??? ??? ??? ??? }
??? ??? ??? ??? continue;
??? ??? ??? }
??? ??? }

??? ??? /* insert the character into the buffer */

??? ??? flag = TTY_NORMAL;
??? ??? port->icount.rx++;

??? ??? if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
??? ??? ??? dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
??? ??? ??? ??? ch, uerstat);

??? ??? ??? /* check for break */
??? ??? ??? if (uerstat & S3C2410_UERSTAT_BREAK) {
??? ??? ??? ??? dbg("break!\n");
??? ??? ??? ??? port->icount.brk++;
??? ??? ??? ??? if (uart_handle_break(port))
??? ??? ??? ??? ??? goto ignore_char;
??? ??? ??? }

??? ??? ??? if (uerstat & S3C2410_UERSTAT_FRAME)
??? ??? ??? ??? port->icount.frame++;
??? ??? ??? if (uerstat & S3C2410_UERSTAT_OVERRUN)
??? ??? ??? ??? port->icount.overrun++;

??? ??? ??? uerstat &= port->read_status_mask;

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

??? ??? if (uart_handle_sysrq_char(port, ch))
??? ??? ??? goto ignore_char;

??? ??? uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN,
??? ??? ??? ??? ?ch, flag);

?ignore_char:
??? ??? continue;
??? }
??? tty_flip_buffer_push(tty);

?out:
??? return IRQ_HANDLED;
}


static irqreturn_t s3c24xx_serial_tx_chars (int irq, void *id)
{
??? struct s3c24xx_uart_port *ourport = id;
??? struct uart_port *port = &ourport->port;
??? struct circ_buf *xmit = &port->info->xmit;
??? int count = 256;

??? if (port->x_char) {
??? ??? wr_regb(port, S3C2410_UTXH, port->x_char);
??? ??? port->icount.tx++;
??? ??? port->x_char = 0;
??? ??? goto out;
??? }

??? /* if there isnt anything more to transmit, or the uart is now
??? ?* stopped, disable the uart and exit
??? */

??? if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
??? ??? s3c24xx_serial_stop_tx(port);
??? ??? goto out;
??? }

??? /* try and drain the buffer... */

??? while (!uart_circ_empty(xmit) && count-- > 0) {
??? ??? if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
??? ??? ??? break;

??? ??? wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
??? ??? xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
??? ??? port->icount.tx++;
??? }

??? if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
??? ??? uart_write_wakeup(port);

??? if (uart_circ_empty(xmit))
??? ??? s3c24xx_serial_stop_tx(port);

?out:
??? return IRQ_HANDLED;
}


  • 串口測試
getty 功能說明:設置終端機模式,連線速率和管制線路,可是s3c2440_serial1無法工作,原因可能是由于開發板上僅僅有一個串口。







linux串口驅動分析


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 91精品国产高清91久久久久久 | 精品免费久久 | 青青青国产色视频在线观看 | 全部免费的毛片在线看青青 | 青青影院一区二区免费视频 | 亚洲国产精品a一区二区三区 | 国产一区视频在线 | 久久久久久久久国产 | 成人欧美一区二区三区视频不卡 | 亚洲精品女同中文字幕在线 | 美女被爆羞羞视频网站视频 | 国产成人青青热久免费精品 | 久久精品国产亚洲麻豆 | 99久久精品无码一区二区毛片 | 中文字幕日韩欧美 | www4虎| 久久久久久网 | 91精品国产免费网站 | 国内精品自在自线香蕉 | 亚洲欧美精品一区二区 | 国产欧美精品午夜在线播放 | 91色综合综合热五月激情 | 欧美视频在线观看 | 99精品国产三级在线观看 | 欧美一区三区 | 色视频国产 | 国产成人精品福利色多多 | 精品国产日韩亚洲一区二区 | 九九九好热在线 | 亚洲人和日本人hd | 亚洲综合欧美在线 | 成人国产三级在线播放 | 日韩深夜视频 | 印度老妇bbwfreexxx | 日本免费不卡在线一区二区三区 | 四虎永久免费地址ww417 | 性欧美欧美之巨大69 | 美女羞羞视频 | 久久亚洲免费视频 | 97视频在线免费观看 | 国产精品视频偷伦精品视频 |