處理過幾個串口的問題,這幾天才稍微有了些時間來整理一下這一塊的東西。
目錄暫時大致分為以下幾部分吧:
0,背景
1,串口驅動的結構
2,串口驅動的實現
3,關於TTY core
---------------------------------------------------------------------------------------------------
0,背景
往常review這塊代碼的時候,經常會被linux代碼樹中東一片西一片的uart,tty,serial device的代碼搞的暈頭轉向。參照linux的驅動裝置模型,其實串口驅動被一層層的完美的組裝在一起。
對於用於,直接接觸的是串口(serial interface),這是在電腦或嵌入式裝置上一種應用非常廣泛的一個裝置介面。我們嘴裡經常嘮叨的UART其實只是串口通訊的一種硬體實現方式而已,只不過最早最廣泛使用的是這一種方式,所以大家聽到的比較多。現在在串口後面的硬體實現方式越來越多:紅外線裝置,usb裝置等等,口那幾根線後面接的chip的種類也越來越多,每種片子都對需要不同的驅動來工作。Linux領域的大師們就根據這些情況以及發展趨勢,將一些通用的介面抽象出來,形成一個支援多種串列通訊的子系統-TTY Core,當然也提供了通用的支援linux 裝置模型的介面方便編寫符合linux裝置模型規範的驅動代碼,:).
note:tty並不是完全指代串口的底層抽象,後面會稍加闡述。
1,串口驅動的結構
對於一般的UART串口,它的實現分為三層,最底層是TTY Core,中間是UART Core,最上層是特定的晶片驅動。
最上層的驅動使用UART Core的API簡單直觀的完成裝置和驅動的註冊,UART Core利用TTY Core中API最終實現裝置驅動的註冊。
2,串口驅動的實現
系統啟動時,tty會作為一個char裝置早早的註冊到kernel(driver/char/tty_io.c),因為它是抽象的最純粹的,:)。
UART Core沒有作為一個模組註冊到kernel,它實現了一些通用的API來供使用到UART的串口裝置來調用。
最上層的晶片驅動就根據晶片的具體特性和參考串列通訊的基本模式依賴UART Core的一些API實現相應的功能供使用者層配置和使用。主要是實現一些tty_operations裡面聲明的一些回呼函數,如果使用UART裝置,有一些實現也會在UART Core裡面實現。
struct tty_operations {
struct tty_struct * (*lookup)(struct tty_driver *driver,
struct inode *inode, int idx);
int (*install)(struct tty_driver *driver, struct tty_struct *tty);
void (*remove)(struct tty_driver *driver, struct tty_struct *tty);
int (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp);
void (*shutdown)(struct tty_struct *tty);
void (*cleanup)(struct tty_struct *tty);
int (*write)(struct tty_struct * tty,
const unsigned char *buf, int count);
int (*put_char)(struct tty_struct *tty, unsigned char ch);
void (*flush_chars)(struct tty_struct *tty);
int (*write_room)(struct tty_struct *tty);
int (*chars_in_buffer)(struct tty_struct *tty);
int (*ioctl)(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg);
long (*compat_ioctl)(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg);
void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
void (*throttle)(struct tty_struct * tty);
void (*unthrottle)(struct tty_struct * tty);
void (*stop)(struct tty_struct *tty);
void (*start)(struct tty_struct *tty);
void (*hangup)(struct tty_struct *tty);
int (*break_ctl)(struct tty_struct *tty, int state);
void (*flush_buffer)(struct tty_struct *tty);
void (*set_ldisc)(struct tty_struct *tty);
void (*wait_until_sent)(struct tty_struct *tty, int timeout);
void (*send_xchar)(struct tty_struct *tty, char ch);
int (*tiocmget)(struct tty_struct *tty, struct file *file);
int (*tiocmset)(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear);
int (*resize)(struct tty_struct *tty, struct winsize *ws);
int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct tty_driver *driver, int line, char *options,
void *rx_callback);
int (*poll_get_char)(struct tty_driver *driver, int line);
void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
#endif
const struct file_operations *proc_fops;
};
3,關於TTY core
TTY在linux領域指的是終端,它可以指代任意終端裝置,包括串口裝置,console裝置等等。
在/dev目錄下,大家如果看到ttyS*就表示指代的是串口裝置(S'serial' tty),而pty就指的是虛擬/偽終端裝置(Pseudo tty)
TTY 子系統使用tty_driver來表示一個在它這個層次抽象出來的裝置驅動,TTY實現了以下API來完成一個終端裝置驅動的註冊:
1)alloc_tty_driver()
2)tty_set_operations():建立tty entry point
3)tty_register_driver()
4)tty_register_device()
這樣讓一個終端驅動的註冊變的十分清晰。