四.TTY層內(nèi)核代碼TTY驅(qū)動程序有三種:控制臺、串口和pty。在此我們主要分析Mini2440串口驅(qū)動。
本文引用地址:http://cafeforensic.com/article/201611/319917.htm我們現(xiàn)在跟蹤uart_register_driver和uart_add_one_port發(fā)現(xiàn),他們的原函數(shù)定義在TTY層驅(qū)動serial_core.o中。
int uart_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal = NULL;
int i, retval;
BUG_ON(drv->state);
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
retval = -ENOMEM;
if (!drv->state)
goto out;
normal = alloc_tty_driver(drv->nr); //分配TTY驅(qū)動
if (!normal)
goto out;
drv->tty_driver = normal;
normal->owner = drv->owner;
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;
normal->major = drv->major;
normal->minor_start = drv->minor;
normal->type = TTY_DRIVER_TYPE_SERIAL;
normal->subtype = SERIAL_TYPE_NORMAL;
normal->init_termios = tty_std_termios; //初始的termios
normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;//控制模式設(shè)置
normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600; //設(shè)置輸入/出速度
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
normal->driver_state = drv; //私有數(shù)據(jù)
tty_set_operations(normal, &uart_ops); //設(shè)置TTY驅(qū)動操作
for (i = 0; i < drv->nr; i++) { //初始化UART狀態(tài)
struct uart_state *state = drv->state + i;
struct tty_port *port = &state->port;
tty_port_init(port);
port->close_delay = 500;
port->closing_wait = 30000;
tasklet_init(&state->tlet, uart_tasklet_action,
(unsigned long)state);
}
retval = tty_register_driver(normal); //注冊TTY驅(qū)動
out:
if (retval < 0) {
put_tty_driver(normal);
kfree(drv->state);
}
return retval;
}
在上面uart_register_driver這個函數(shù)里我們首先分配了TTY驅(qū)動,然后對其進(jìn)行填充,初始的termios,并設(shè)置TTY驅(qū)動操作,最后注冊TTY驅(qū)動。其中設(shè)置TTY驅(qū)動操作時用到uart_ops,我們看看這個uart_ops到底是什么。
static const struct tty_operations uart_ops = {
.open = uart_open,
.close = uart_close,
.write = uart_write,
.put_char = uart_put_char,
.flush_chars = uart_flush_chars,
.write_room = uart_write_room,
.chars_in_buffer= uart_chars_in_buffer,
.flush_buffer = uart_flush_buffer,
.ioctl = uart_ioctl,
.throttle = uart_throttle,
.unthrottle = uart_unthrottle,
.send_xchar = uart_send_xchar,
.set_termios = uart_set_termios,
.set_ldisc = uart_set_ldisc,
.stop = uart_stop,
.start = uart_start,
.hangup = uart_hangup,
.break_ctl = uart_break_ctl,
.wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
.proc_fops = &uart_proc_fops,
#endif
.tiocmget = uart_tiocmget,
.tiocmset = uart_tiocmset,
#ifdef CONFIG_CONSOLE_POLL
.poll_init = uart_poll_init,
.poll_get_char = uart_poll_get_char,
.poll_put_char = uart_poll_put_char,
#endif
};
終端設(shè)備可以完成收發(fā)數(shù)據(jù)的功能,當(dāng)用戶在有數(shù)據(jù)發(fā)送給終端設(shè)備時候,通過”write()系統(tǒng)調(diào)用—tty核心—線路規(guī)程”的層層調(diào)用,最終調(diào)用tty_driver結(jié)構(gòu)體中的write()函數(shù)完成發(fā)送。因?yàn)閭鬏斔俣群蛅ty硬件緩沖區(qū)容量的原因,不是所有的寫程序要求的字符都可以在調(diào)用寫函數(shù)時候被發(fā)送出去,因此,寫函數(shù)應(yīng)當(dāng)返回能夠發(fā)給硬件的字節(jié)數(shù)以便用戶程序檢查是否所有的數(shù)據(jù)被真正寫入。如果在write()調(diào)用期間發(fā)生任何錯誤,一個負(fù)的錯誤碼應(yīng)當(dāng)被返回。在上面的uart_ops結(jié)構(gòu)體中,我們先看看寫函數(shù)uart_write的實(shí)現(xiàn)吧。
static int uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
struct uart_state *state = tty->driver_data; //獲取設(shè)備私有信息結(jié)構(gòu)體
struct uart_port *port;
struct circ_buf *circ;
unsigned long flags;
int c, ret = 0;
if (!state) {
WARN_ON(1);
return -EL3HLT;
}
port = state->uart_port; //UART端口
circ = &state->xmit; //數(shù)據(jù)緩沖區(qū)
if (!circ->buf)
return 0;
spin_lock_irqsave(&port->lock, flags); //獲取UART端口操作的鎖
while (1) {
//返回可用緩存空間的大小
c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
if (count < c)
c = count;
if (c <= 0) //緩存區(qū)太小則退出
break;
//將用戶空間buf中大小為c的內(nèi)容拷貝到緩存中
memcpy(circ->buf + circ->head, buf, c);
circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
buf += c; //緩存區(qū)指針后移
count -= c; //當(dāng)一次發(fā)送的字節(jié)過多,需要分次發(fā)送
ret += c; //已經(jīng)發(fā)送的字節(jié)數(shù)
}
spin_unlock_irqrestore(&port->lock, flags); //釋放UART端口操作的鎖
uart_start(tty); //開始發(fā)送
return ret;
}
根據(jù)上面對uart_write的分析,我們知道tty_driver的write()函數(shù)接收三個參數(shù)tty_struct,發(fā)送數(shù)據(jù)指針和要發(fā)送的字節(jié)數(shù)。uart_state作為這個驅(qū)動tty的私有數(shù)據(jù),其中circ_buf定義了緩沖區(qū),我們向這個緩沖區(qū)拷貝待發(fā)送的內(nèi)容后,執(zhí)行uart_start(tty)進(jìn)行發(fā)送數(shù)據(jù)。那我們繼續(xù)看跟蹤uart_start函數(shù)
static void uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags); //獲取UART端口操作的鎖
__uart_start(tty);
spin_unlock_irqrestore(&port->lock, flags); //釋放UART端口操作的鎖
}
static void __uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
if (!uart_circ_empty(&state->xmit) && state->xmit.buf &&
!tty->stopped && !tty->hw_stopped) //緩沖區(qū)有數(shù)據(jù)并開啟發(fā)送狀態(tài)
port->ops->start_tx(port); //調(diào)用uart_ops下的start_tx,即s3c24xx_serial_start_tx
}
注意__uart_start函數(shù)中的port->ops->start_tx(port)便實(shí)現(xiàn)了tty層和uart層的相連,由tty層的write()調(diào)用uart層的write()。
好了,上面講的是發(fā)送數(shù)據(jù),讀者可能注意到struct tty_operations uart_ops中沒有提到read()函數(shù)。因?yàn)榘l(fā)送是用戶主動的,而接收拾用戶調(diào)用read()從一片緩沖區(qū)讀取已經(jīng)放好的數(shù)據(jù),這個緩沖區(qū)由struct tty_flip_buffer結(jié)構(gòu)體實(shí)現(xiàn)。因?yàn)閠ty核提供了這樣的緩沖邏輯,所以每個tty驅(qū)動并非一定要實(shí)現(xiàn)它自身的緩沖邏輯。Tty驅(qū)動不需要關(guān)注struct tty_flip_buffe的細(xì)節(jié),從tty驅(qū)動接收到的來自硬件層的字符將被tty_insert_filp_char()函數(shù)插入filp緩沖區(qū)。如果傳輸?shù)淖止?jié)數(shù)count大于或等于TTY_FLIPBUF_SIEZE,這個flip緩沖區(qū)就需要被刷新到用戶,刷新是通過調(diào)用tty_flip_buffer_push()實(shí)現(xiàn)的。
接著,我們繼續(xù)看struct tty_operations uart_ops中對termios的設(shè)置函數(shù)set_termios,即uart_set_termios。這個set_termios需要根據(jù)用戶對termios的設(shè)置完成實(shí)際的硬件設(shè)置。新的設(shè)置被保存在tty_struct中,舊的設(shè)置被保存在old參數(shù)中,若新舊參數(shù)相同,則什么都不需要做,對于被改的設(shè)置,需要完成硬件上的設(shè)置。好了,下面我們還是看看uart_set_termios的實(shí)現(xiàn)吧。
static void uart_set_termios(struct tty_struct *tty,
struct ktermios *old_termios)
{
struct uart_state *state = tty->driver_data; //獲取私有數(shù)據(jù)
unsigned long flags;
unsigned int cflag = tty->termios->c_cflag; //獲取當(dāng)前線路設(shè)置
#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
//如果新舊線路設(shè)置的控制狀態(tài),輸入輸出速度等信息一樣,則退出
if ((cflag ^ old_termios->c_cflag) == 0 &&
tty->termios->c_ospeed == old_termios->c_ospeed &&
tty->termios->c_ispeed == old_termios->c_ispeed &&
RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) {
return;
}
uart_change_speed(state, old_termios); //用新的線路規(guī)程的速度更新舊的線路規(guī)程
//處理波特率為B0情況
if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
uart_clear_mctrl(state->uart_port, TIOCM_RTS | TIOCM_DTR);
/處理波特率為非B0情況
if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
unsigned int mask = TIOCM_DTR;
if (!(cflag & CRTSCTS) ||
!test_bit(TTY_THROTTLED, &tty->flags))
mask |= TIOCM_RTS;
uart_set_mctrl(state->uart_port, mask); //設(shè)置modem控制
}
//處理無數(shù)據(jù)流控制情況
if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
spin_lock_irqsave(&state->uart_port->lock, flags);
tty->hw_stopped = 0;
__uart_start(tty);
spin_unlock_irqrestore(&state->uart_port->lock, flags);
}
//處理有數(shù)據(jù)流控制情況
if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
spin_lock_irqsave(&state->uart_port->lock, flags);
if (!(state->uart_port->ops->get_mctrl(state->uart_port) & TIOCM_CTS)) {
tty->hw_stopped = 1;
state->uart_port->ops->stop_tx(state->uart_port);
}
spin_unlock_irqrestore(&state->uart_port->lock, flags);
}
}
好了我們已經(jīng)講解了write,set_termiox,下面我們講講tiocmget和tiocmset。Tiocmget()函數(shù)用于獲取tty設(shè)備的線路設(shè)置,對應(yīng)的tiocmset()用于設(shè)置tty設(shè)備的線路設(shè)置。
static int uart_tiocmget(struct tty_struct *tty, struct file *file)
{
struct uart_state *state = tty->driver_data;
struct tty_port *port = &state->port;
struct uart_port *uport = state->uart_port;
int result = -EIO;
mutex_lock(&port->mutex); //獲取對tty_port操作的鎖
if ((!file || !tty_hung_up_p(file)) &&
!(tty->flags & (1 << TTY_IO_ERROR))) {
result = uport->mctrl;
spin_lock_irq(&uport->lock);
result |= uport->ops->get_mctrl(uport); //調(diào)用UART層get_mctrl獲取modem控制
spin_unlock_irq(&uport->lock);
}
mutex_unlock(&port->mutex); //釋放對tty_port操作的鎖
return result;
}
static int uart_tiocmset(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear)
{
struct uart_state *state = tty->driver_data;
struct uart_port *uport = state->uart_port;
struct tty_port *port = &state->port;
int ret = -EIO;
mutex_lock(&port->mutex); //獲取對tty_port操作的鎖
if ((!file || !tty_hung_up_p(file)) &&
!(tty->flags & (1 << TTY_IO_ERROR))) {
uart_update_mctrl(uport, set, clear); //獲取modem控制
ret = 0;
}
mutex_unlock(&port->mutex); //釋放對tty_port操作的鎖
return ret;
}
上面uart_tiocmset中調(diào)用了uart_update_mctrl(uport, set, clear)函數(shù),它最終是通過調(diào)用port->ops->set_mctrl(port, port->mctrl)完成,而set_mctrl在UART層的uart_ops實(shí)現(xiàn)了。
綜上,TTY層的ops中的uart_tiocmget和uart_tiocmset其實(shí)最終是調(diào)用UART層uart_ops中的get_mctrl和set_mctrl實(shí)現(xiàn)的。
當(dāng)用戶在tty設(shè)備節(jié)點(diǎn)上進(jìn)行ioctl調(diào)用時,tty_operations中的ioctl()函數(shù)會被tty核心調(diào)用。我們接下來看看struct tty_operations uart_ops下的.ioctl也就是uart_ioctl。
static int uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct uart_state *state = tty->driver_data;
struct tty_port *port = &state->port;
void __user *uarg = (void __user *)arg;
int ret = -ENOIOCTLCMD;
switch (cmd) { //這些ioctl不依賴硬件
case TIOCGSERIAL: //獲得串口線信息
ret = uart_get_info(state, uarg);
break;
case TIOCSSERIAL: //設(shè)置串口線信息
ret = uart_set_info(state, uarg);
break;
case TIOCSERCONFIG: //自動配置
ret = uart_do_autoconfig(state);
break;
case TIOCSERGWILD:
case TIOCSERSWILD:
ret = 0;
break;
}
if (ret != -ENOIOCTLCMD)
goto out;
if (tty->flags & (1 << TTY_IO_ERROR)) {
ret = -EIO;
goto out;
}
switch (cmd) { //這些ioctl依賴硬件
case TIOCMIWAIT: //等待MSR改變
ret = uart_wait_modem_status(state, arg);
break;
case TIOCGICOUNT: //獲得中斷計數(shù)
ret = uart_get_count(state, uarg);
break;
}
if (ret != -ENOIOCTLCMD)
goto out;
mutex_lock(&port->mutex);
if (tty_hung_up_p(filp)) {
ret = -EIO;
goto out_up;
}
switch (cmd) { //這些ioctl依賴硬件,并且需要保護(hù),房子tty被掛起
case TIOCSERGETLSR: //獲得這個tty設(shè)備的線路狀態(tài)寄存器LSR的值
ret = uart_get_lsr_info(state, uarg);
break;
default: {
struct uart_port *uport = state->uart_port;
if (uport->ops->ioctl)
ret = uport->ops->ioctl(uport, cmd, arg);
break;
}
}
out_up:
mutex_unlock(&port->mutex);
out:
return ret;
}
當(dāng)TTY核心想知道由TTY驅(qū)動程序提供的可用寫入緩沖區(qū)的大小時,就會調(diào)用write_room。在清空寫緩沖區(qū),或者調(diào)用write函數(shù)向緩沖區(qū)添加數(shù)據(jù)時,該值是變化的。接下來我們看看TTY層ops中write_room,也就是uart_write_room。跟蹤發(fā)現(xiàn)其實(shí)這個函數(shù)實(shí)現(xiàn)主要是把緩沖區(qū)頭尾相減得到剩余空間大小。
除了write_room外,還有其他一些緩沖函數(shù),例如TTY層ops中chars_in_buffer,也就是uart_chars_in_buffer,當(dāng)tty核心在tty驅(qū)動程序的寫緩沖區(qū)中還有多少個需要傳輸?shù)淖址麜r調(diào)用該函數(shù)。
除此之外TTY層ops中還有三個回調(diào)函數(shù),用來刷新驅(qū)動程序保存的任何數(shù)據(jù),并不一定要實(shí)現(xiàn),但是如果tty驅(qū)動程序能在發(fā)送給硬件前緩沖數(shù)據(jù),還是推薦實(shí)現(xiàn)它們的,它們分別是flush_buffer,wait_until_sent,flush_buffer。
回顧一下,我們在TTY層的ops中,主要講了write,set_termiox, tiocmget,tiocmset,ioctl,五個函數(shù),還簡單介紹了write_room,chars_in_buffer,flush_buffer,wait_until_sent,flush_buffer五個函數(shù)。到目前為止,我們已經(jīng)分析好了uart_register_driver函數(shù),現(xiàn)在該分析uart_add_one_port函數(shù)了。
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
{
struct uart_state *state;
struct tty_port *port;
int ret = 0;
struct device *tty_dev;
BUG_ON(in_interrupt());
if (uport->line >= drv->nr)
return -EINVAL;
state = drv->state + uport->line;
port = &state->port;
mutex_lock(&port_mutex);
mutex_lock(&port->mutex);
if (state->uart_port) {
ret = -EINVAL;
goto out;
}
state->uart_port = uport;
state->pm_state = -1;
uport->cons = drv->cons;
uport->state = state;
//如果這個端口是控制臺,那么這個鎖就已經(jīng)初始化了
if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
spin_lock_init(&uport->lock);
lockdep_set_class(&uport->lock, &port_lock_key);
}
uart_configure_port(drv, state, uport); //配置端口
tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);//注冊端口
if (likely(!IS_ERR(tty_dev))) {
device_init_wakeup(tty_dev, 1);
device_set_wakeup_enable(tty_dev, 0);
} else
printk(KERN_ERR "Cannot register tty device on line %dn",
uport->line);
//確保UPF_DEAD沒有被置位
uport->flags &= ~UPF_DEAD;
out:
mutex_unlock(&port->mutex);
mutex_unlock(&port_mutex);
return ret;
}
對于uart_add_one_port,我們發(fā)現(xiàn)其中最核心的一句代碼就是tty_register_device,僅有tty_driver是不夠的,驅(qū)動必須依附于設(shè)備,tty_register_device函數(shù)用于注冊關(guān)聯(lián)于tty_driver的設(shè)備。
總結(jié)下,TTY層的uart_register_driver和uart_register_port最終調(diào)用線路規(guī)程的tty_register_driver和tty_register_device。而tty_register_driver和tty_register_device的實(shí)現(xiàn)在線路規(guī)程中。
對于TTY驅(qū)動主要涉及如下幾個重要結(jié)構(gòu)體,struct tty_struct包含了和打開的tty相關(guān)的所有狀態(tài)信息。其中一個重要的成員就是struct tty_bufhead buf,它是數(shù)據(jù)收集和處理機(jī)制的中樞,其定義如下
struct tty_bufhead {
struct delayed_work work;
spinlock_t lock;
struct tty_buffer *head;
struct tty_buffer *tail;
struct tty_buffer *free;
int memory_used;
};
另一個重要結(jié)構(gòu)體是struct tty_driver,它規(guī)定了tty驅(qū)動程序和高層之間的編程接口。在我們這個TTY層,由uart_register_driver下的tty_register_driver注冊入內(nèi)核,其中這個結(jié)構(gòu)體中的成員部分是通過拷貝uart_driver中的參數(shù)得到。
好了,對于TTY層驅(qū)動,一般而言,我們需要完成如下兩個任務(wù):
其一,終端設(shè)備驅(qū)動模塊的加載函數(shù)和卸載函數(shù),完成注冊和注銷tty_driver,初始化和釋放終端設(shè)備對應(yīng)的tty_driver結(jié)構(gòu)體成員和硬件資源。
其二,實(shí)現(xiàn)tty_operations結(jié)構(gòu)體中的一系列成員函數(shù),主要的是實(shí)現(xiàn)open()、close()、 write()、 tiocmget()、 tiocmset()。
評論