一、前言
前面介紹了 Linux內(nèi)核 的 2 個(gè)驅(qū)動框架—— I2C 和 SPI 膘婶,這 2 個(gè)框架相對簡單一些缺前,直來直去,沒有比較難以理解的點(diǎn)悬襟,層次分明衅码。而今天我們要講述的是我們熟悉的 串口驅(qū)動,該驅(qū)動加框也較之之前的驅(qū)動來說脊岳,復(fù)雜了許多逝段。串口 是我們常用的通訊手段,但其軟件框架在 Linux內(nèi)核 中非常復(fù)雜割捅。當(dāng)然奶躯,這里面也有歷史原因在。本文將簡單地介紹 UART驅(qū)動框架亿驾,希望能夠幫助各位讀者嘹黔。
二、UART驅(qū)動
2.1 tty簡介
串口(UART) 通常是以 tty驅(qū)動 的形式在用戶層表現(xiàn)出來莫瞬,那么我們首先需要知道 tty 是什么東西儡蔓。
tty 是 Linux系統(tǒng) 中出現(xiàn)得最多的一種設(shè)備類型,可以直接查看 /dev目錄 下疼邀,往往可以看到多個(gè) tty設(shè)備節(jié)點(diǎn)喂江。要了解 tty設(shè)備 就需要從歷史的角度說起。
在計(jì)算機(jī)系統(tǒng)中旁振,終端 是 電子設(shè)備获询,用于向系統(tǒng)輸入數(shù)據(jù),或者接收系統(tǒng)發(fā)送的數(shù)據(jù)并顯示出來规求,所以該設(shè)備的作用是用于 人機(jī)交互筐付。而 電傳打字機(jī)(Teletype) 是一種久遠(yuǎn)的 遠(yuǎn)距離信息傳送設(shè)備卵惦,其通過 鍵盤 輸入數(shù)據(jù)阻肿,通過 印字機(jī)器 顯示接收到的數(shù)據(jù)。電傳打字機(jī) 就是一種典型的 終端沮尿,其英語簡稱即 tty丛塌。
按照筆者的理解较解,tty 即用于與 用戶 交互的設(shè)備,其向計(jì)算機(jī)系統(tǒng)輸入數(shù)據(jù)赴邻,也接收來自計(jì)算機(jī)系統(tǒng)的數(shù)據(jù)印衔。有興趣的讀者可以參考鏈接中的文章 Linux TTY framework(1)_基本概念。
對于 Linux系統(tǒng) 來說姥敛,通過 串口 連接的設(shè)備一般都可以作為 終端設(shè)備奸焙。經(jīng)過日積月累,終端彤敛、tty与帆、串口等概念逐漸不再去分。一般來講墨榄,所有 串口設(shè)備 都是 tty設(shè)備玄糟,而 tty設(shè)備 除了 串口外還有其他的具體形式,比如 虛擬終端 袄秩、偽終端等阵翎。
其實(shí)這里還涉及到一個(gè)概念 控制臺(console),但本文目前不做講述之剧,后續(xù)有機(jī)會再寫相關(guān)文章郭卫。
2.2 串口tty框架
在講解相關(guān)細(xì)節(jié)之前,我們需要對 串口tty驅(qū)動 有個(gè)整體性的認(rèn)識背稼,這樣有非常助于后面理解代碼細(xì)節(jié)箱沦。在看細(xì)節(jié)之前,我們需要知道:
- 整個(gè)驅(qū)動的整體架構(gòu)是如何組織的
- 數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系是如何
- 函數(shù)的傳遞是如何遞進(jìn)的
下面將展示一張圖雇庙,該圖有助于我們理解
我們可以看到:
-
從左往右 看谓形,整個(gè)驅(qū)動可以分為 3個(gè)層次,分為為 字符設(shè)備層疆前、tty層 和 串口硬件層寒跳。
字符設(shè)備層 是 tty驅(qū)動 作為 字符設(shè)備 直接與應(yīng)用層進(jìn)行交互,中間通過 tty層 進(jìn)行抽象竹椒,使得底層的具體形式可以被解耦童太。最終是底層硬件的 串口層,該層負(fù)責(zé)數(shù)據(jù)的設(shè)備之間的交互及一系列管理操作的具體實(shí)現(xiàn)胸完。 - 從上往下 看书释,驅(qū)動主要由幾個(gè)數(shù)據(jù)結(jié)構(gòu)組成,分別為 UART_driver赊窥、UART_state 和 tty_driver爆惧。UART_driver 是全局的 根數(shù)據(jù)結(jié)構(gòu),所有的結(jié)構(gòu)體都由其來進(jìn)行保存和控制锨能。tty_driver 則是對 tty層的具體實(shí)現(xiàn)扯再,最后 UART_state 和 Uart_pot 則是底層驅(qū)動的具體實(shí)現(xiàn)芍耘。
2.3 串口tty驅(qū)動
本節(jié)將對代碼進(jìn)行講述,其中我們選擇 8250 這個(gè)串口IP的代碼作為示例來進(jìn)行講述熄阻。當(dāng)然只是為了學(xué)習(xí)驅(qū)動的實(shí)現(xiàn)斋竞,其他的串口驅(qū)動讀者可以按照自己的理解再去閱讀。
本節(jié)會分別按照 初始化 和 讀/寫 來進(jìn)行講述秃殉。
2.3.1 串口硬件初始化
串口驅(qū)動的框架一大部分是在初始化階段進(jìn)行坝初,對 初始化 進(jìn)行梳理是必要的階段。下面先來看看代碼調(diào)用圖譜钾军。
首先是對 UART_driver結(jié)構(gòu)體 進(jìn)行創(chuàng)建和相關(guān)初始化脖卖,其負(fù)責(zé)高度的軟件抽象和相關(guān)軟件邏輯。其代碼調(diào)用圖譜如下:
->serial8250_init
->serial8250_isa_init_ports(初始化serial8250_ports巧颈,數(shù)據(jù)類型為uart_8250_port)
->serial8250_init_port(將serial8250_pops賦值給uart_port->ops)
->univ8250_rsa_support
->uart_register_driver(注冊uart_driver畦木,即全局變量serial8250_reg,將uart_ops設(shè)置給tty_driver)
->alloc_tty_driver(分配uart_driver->uart_state)
->tty_port_init(初始化uart_driver->uart_state->tty_port砸泛,tty_port的操作集為uart_port_ops)
->tty_register_driver(注冊tty_driver十籍,將tty_driver加入全局鏈表tty_drivers)
/* 8250_core */
/* 初始化8250串口框架 */
module_init(serial8250_init);
static int __init serial8250_init(void)
{
int ret;
......
/* 初始化各個(gè)串口的UART_port數(shù)據(jù)結(jié)構(gòu) */
serial8250_isa_init_ports();
......
/* 注冊UART驅(qū)動即UART_driver */
serial8250_reg.nr = UART_NR;
ret = uart_register_driver(&serial8250_reg);
......
serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);
......
}
/* 初始化各個(gè)串口的UART_port數(shù)據(jù)結(jié)構(gòu) */
static void __init serial8250_isa_init_ports(void)
{
struct uart_8250_port *up;
static int first = 1;
int i, irqflag = 0;
......
/* 初始化每個(gè)UART_port的操作函數(shù)集 */
for (i = 0; i < nr_uarts; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
struct uart_port *port = &up->port;
/* 設(shè)置當(dāng)前UART_port的編號 */
port->line = i;
/* 初始化UART_port的ops成員 */
serial8250_init_port(up);
{
struct uart_port *port = &up->port;
port->ops = &serial8250_pops;
......
}
/* 此時(shí)port->ops為serial8250_pops,所以base_ops指向了serial8250_pops */
if (!base_ops)
base_ops = port->ops;
/* 將UART_port的ops成員指向univ8250_port_ops */
port->ops = &univ8250_port_ops;
......
/* 設(shè)置uart_8250_port的ops成員指向了univ8250_driver_ops唇礁,該成員在設(shè)置中斷時(shí)有用到 */
up->ops = &univ8250_driver_ops;
......
}
/*
univ8250_port_ops的成員賦值為*base_ops指向的地址勾栗,即serial8250_pops
所以此時(shí)UART_port的ops成員指向serial8250_pops
*/
univ8250_port_ops = *base_ops;
/* 設(shè)置UART_port->ops的部分成員 */
univ8250_rsa_support(&univ8250_port_ops);
{
ops->config_port = univ8250_config_port;
ops->request_port = univ8250_request_port;
ops->release_port = univ8250_release_port;
}
......
}
/* 注冊UART_driver驅(qū)動 */
int uart_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal;
int i, retval;
/* 為UART_driver的每個(gè)UART_state分配空間,每個(gè)串口硬件都有UART_state */
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
......
/* 為串口驅(qū)動UART_driver分配一個(gè)tty_driver */
normal = alloc_tty_driver(drv->nr);
{
struct tty_driver *ret = tty_alloc_driver(lines, 0);
......
return ret;
}
......
/* 設(shè)置UART_driver的tty_driver成員 */
drv->tty_driver = normal;
/* 初始化相關(guān)成員 */
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;
/* 這里的設(shè)備號會在后續(xù)的open過程中使用到 */
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;
normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
normal->driver_state = drv;
/* 設(shè)置tty_driver的ops成員指向struct tty_operations uart_ops */
tty_set_operations(normal, &uart_ops);
{
driver->ops = op;
};
/* 初始化每個(gè)UART_state的tty_port成員 */
for (i = 0; i < drv->nr; i++) {
struct uart_state *state = drv->state + i;
struct tty_port *port = &state->port;
/* 初始化每個(gè)tty_port */
tty_port_init(port);
{
memset(port, 0, sizeof(*port));
tty_buffer_init(port);
/* 以下為tty_buffer_init函數(shù)體 */
{
struct tty_bufhead *buf = &port->buf;
mutex_init(&buf->lock);
tty_buffer_reset(&buf->sentinel, 0);
buf->head = &buf->sentinel;
buf->tail = &buf->sentinel;
init_llist_head(&buf->free);
atomic_set(&buf->mem_used, 0);
atomic_set(&buf->priority, 0);
INIT_WORK(&buf->work, flush_to_ldisc);//該work_queue盏筐,后面的讀取數(shù)據(jù)的時(shí)候要用到
buf->mem_limit = TTYB_DEFAULT_MEM_LIMIT;
}
init_waitqueue_head(&port->open_wait);
init_waitqueue_head(&port->delta_msr_wait);
mutex_init(&port->mutex);
mutex_init(&port->buf_mutex);
spin_lock_init(&port->lock);
port->close_delay = (50 * HZ) / 100;
port->closing_wait = (3000 * HZ) / 100;
/* 設(shè)置tty_port的client_ops指向default_client_ops围俘,會在接收和發(fā)送數(shù)據(jù)時(shí)使用到 */
port->client_ops = &default_client_ops;
kref_init(&port->kref);
}
/* 設(shè)置tty_port的ops成員指向uart_port_ops */
port->ops = &uart_port_ops;
}
retval = tty_register_driver(normal);
......
}
/* 注冊tty_driver驅(qū)動 */
int tty_register_driver(struct tty_driver *driver)
{
int error;
int i;
dev_t dev;
struct device *d;
/* 分配設(shè)備好 */
if (!driver->major) {
error = alloc_chrdev_region(&dev, driver->minor_start,
driver->num, driver->name);
if (!error) {
driver->major = MAJOR(dev);
driver->minor_start = MINOR(dev);
}
} else {
dev = MKDEV(driver->major, driver->minor_start);
error = register_chrdev_region(dev, driver->num, driver->name);
}
if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {
/* 添加tty的字符設(shè)備 */
error = tty_cdev_add(driver, dev, 0, driver->num);
{
int err;
driver->cdevs[index] = cdev_alloc();
......
/* 設(shè)置字符設(shè)備的操作集為tty_fops,該操作集用于tty設(shè)備和應(yīng)用層交互 */
driver->cdevs[index]->ops = &tty_fops;
driver->cdevs[index]->owner = driver->owner;
/* 添加字符設(shè)備 */
err = cdev_add(driver->cdevs[index], dev, count);
......
return err;
}
......
}
/* 將當(dāng)前的tty_driver添加全局的tty_drivers鏈表 */
mutex_lock(&tty_mutex);
list_add(&driver->tty_drivers, &tty_drivers);
mutex_unlock(&tty_mutex);
/* 注冊每一個(gè)tty設(shè)備 */
if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
for (i = 0; i < driver->num; i++) {
d = tty_register_device(driver, i, NULL);
......
}
}
}
......
return 0;
......
}
上面是針對 8250串口核心 做的一些基本的初始化琢融。按照筆者理解界牡,在實(shí)際應(yīng)用中用戶有可能需要加載其他串口驅(qū)動來修改串口的一些屬性,所以當(dāng)用戶執(zhí)行驅(qū)動裝載時(shí)有以下的初始化步驟漾抬,同樣也是針對 UART_port 和 UART_state 的初始化和操作宿亡。其調(diào)用譜圖和代碼示例如下:
dw8250_probe
->serial8250_register_8250_port
->serial8250_find_match_or_unused(從全局serial8250_ports找出可以使用的元素,該元素可以理解為uart_port)
->uart_add_one_port(填充uart_port)
->tty_port_register_device_attr_serdev
->tty_port_link_device
->tty_register_device_attr
->tty_cdev_add(將tty_ops賦給tty_driver的cdec成員)
/* 初始化用戶的串口硬件模塊 */
static int dw8250_probe(struct platform_device *pdev)
{
struct uart_8250_port uart = {};
struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
int irq = platform_get_irq(pdev, 0);
struct uart_port *p = &uart.port;
struct device *dev = &pdev->dev;
struct dw8250_data *data;
int err;
u32 val;
......
/* 對p指向uart_port結(jié)構(gòu)體進(jìn)行初始化纳令,根據(jù)用戶自己的進(jìn)行設(shè)置 */
spin_lock_init(&p->lock);
p->mapbase = regs->start;
p->irq = irq; //串口中斷號
p->handle_irq = dw8250_handle_irq; //串口中斷處理函數(shù)挽荠,會在串口中斷時(shí)執(zhí)行
p->pm = dw8250_do_pm;
p->type = PORT_8250;
p->flags = UPF_SHARE_IRQ | UPF_FIXED_PORT;
p->dev = dev;
p->iotype = UPIO_MEM;
p->serial_in = dw8250_serial_in; //串口輸入寄存器設(shè)置函數(shù)
p->serial_out = dw8250_serial_out; //串口輸出寄存器設(shè)置函數(shù)
p->set_ldisc = dw8250_set_ldisc; //串口線路規(guī)程設(shè)置函數(shù)
p->set_termios = dw8250_set_termios; //串口中斷參數(shù)規(guī)程設(shè)置函數(shù)
/* 設(shè)置寄存器地址 */
p->membase = devm_ioremap(dev, regs->start, resource_size(regs));
......
/* 分配私有數(shù)據(jù) */
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
......
data->dma.fn = dw8250_fallback_dma_filter;
data->usr_reg = DW_UART_USR;
p->private_data = data;
......
/* 注冊用戶自己的串口驅(qū)動 */
data->line = serial8250_register_8250_port(&uart);
if (data->line < 0) {
err = data->line;
goto err_reset;
}
......
}
/* 注冊用戶的串口驅(qū)動 */
int serial8250_register_8250_port(struct uart_8250_port *up)
{
struct uart_8250_port *uart;
int ret = -ENOSPC;
......
mutex_lock(&serial_mutex);
/* 從8250_core中找出一個(gè)沒有用且匹配的uart_8250_port */
uart = serial8250_find_match_or_unused(&up->port);
/* 對uart_8250_port 進(jìn)行初始化 */
if (uart && uart->port.type != PORT_8250_CIR) {
......
/* 初始化找到的uart_port數(shù)據(jù)結(jié)構(gòu)uart->port為uart_port數(shù)據(jù)結(jié)構(gòu) */
uart->port.iobase = up->port.iobase;
uart->port.membase = up->port.membase;
uart->port.irq = up->port.irq;
uart->port.irqflags = up->port.irqflags;
uart->port.uartclk = up->port.uartclk;
uart->port.fifosize = up->port.fifosize;
uart->port.regshift = up->port.regshift;
uart->port.iotype = up->port.iotype;
uart->port.flags = up->port.flags | UPF_BOOT_AUTOCONF;
uart->bugs = up->bugs;
uart->port.mapbase = up->port.mapbase;
uart->port.mapsize = up->port.mapsize;
uart->port.private_data = up->port.private_data;
uart->tx_loadsz = up->tx_loadsz;
uart->capabilities = up->capabilities;
uart->port.throttle = up->port.throttle;
uart->port.unthrottle = up->port.unthrottle;
uart->port.rs485_config = up->port.rs485_config;
uart->port.rs485 = up->port.rs485;
uart->dma = up->dma;
if (uart->port.fifosize && !uart->tx_loadsz)
uart->tx_loadsz = uart->port.fifosize;
if (up->port.dev)
uart->port.dev = up->port.dev;
if (up->port.flags & UPF_FIXED_TYPE)
uart->port.type = up->port.type;
/* 設(shè)置uart_port的默認(rèn)屬性 */
serial8250_set_defaults(uart);
if (up->port.serial_in)
uart->port.serial_in = up->port.serial_in;
if (up->port.serial_out)
uart->port.serial_out = up->port.serial_out;
if (up->port.handle_irq)
uart->port.handle_irq = up->port.handle_irq;
if (up->port.set_termios)
uart->port.set_termios = up->port.set_termios;
if (up->port.set_ldisc)
uart->port.set_ldisc = up->port.set_ldisc;
if (up->port.get_mctrl)
uart->port.get_mctrl = up->port.get_mctrl;
if (up->port.set_mctrl)
uart->port.set_mctrl = up->port.set_mctrl;
if (up->port.startup)
uart->port.startup = up->port.startup;
if (up->port.shutdown)
uart->port.shutdown = up->port.shutdown;
if (up->port.pm)
uart->port.pm = up->port.pm;
if (up->port.handle_break)
uart->port.handle_break = up->port.handle_break;
if (up->dl_read)
uart->dl_read = up->dl_read;
if (up->dl_write)
uart->dl_write = up->dl_write;
if (uart->port.type != PORT_8250_CIR) {
if (serial8250_isa_config != NULL)
serial8250_isa_config(0, &uart->port,
&uart->capabilities);
.....
/* 將新的uart_port添加到uart_driver */
ret = uart_add_one_port(&serial8250_reg, &uart->port);
if (ret == 0)
ret = uart->port.line;
} else {
dev_info(uart->port.dev,
"skipping CIR port at 0x%lx / 0x%llx, IRQ %d\n",
uart->port.iobase,
(unsigned long long)uart->port.mapbase,
uart->port.irq);
ret = 0;
}
}
mutex_unlock(&serial_mutex);
return ret;
}
/* 把一個(gè)uart_port添加到uart_driver */
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;
int num_groups;
/* 根據(jù)串口編號獲取對應(yīng)的uart_state和uart_port */
state = drv->state + uport->line;
port = &state->port;
......
/* 設(shè)置新的uart_port到uart_state中,并將對uart_port的state成員進(jìn)行賦值 */
state->uart_port = uport;
uport->state = state;
......
/* 分配并設(shè)置屬性組 */
uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups),
GFP_KERNEL);
if (!uport->tty_groups) {
ret = -ENOMEM;
goto out;
}
uport->tty_groups[0] = &tty_dev_attr_group;
if (uport->attr_group)
uport->tty_groups[1] = uport->attr_group;
/* 將新的uart_port及其屬性注冊到內(nèi)核中 */
tty_dev = tty_port_register_device_attr_serdev(port, drv->tty_driver,
uport->line, uport->dev, port, uport->tty_groups);
......
}
struct device *tty_port_register_device_attr_serdev(struct tty_port *port,
struct tty_driver *driver, unsigned index,
struct device *device, void *drvdata,
const struct attribute_group **attr_grp)
{
struct device *dev;
/* 該函數(shù)設(shè)置tty_drvier的tty_port成員平绩,建立聯(lián)系 */
tty_port_link_device(port, driver, index);
{
if (WARN_ON(index >= driver->num))
return;
driver->ports[index] = port;
}
/* 該函數(shù)會設(shè)置tty_port的一部分成員 */
dev = serdev_tty_port_register(port, device, driver, index);
{
/* 設(shè)置tty_port的client_ops成員 */
port->client_ops = &client_ops;
}
/* 注冊tty_port對應(yīng)的device */
return tty_register_device_attr(driver, index, device, drvdata,
attr_grp);
}
......
struct device *tty_register_device_attr(struct tty_driver *driver,
unsigned index, struct device *device,
void *drvdata,
const struct attribute_group **attr_grp)
{
char name[64];
dev_t devt = MKDEV(driver->major, driver->minor_start) + index;
struct ktermios *tp;
struct device *dev;
int retval;
......
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
......
/* 設(shè)置dev的相關(guān)屬性 */
dev->devt = devt;
dev->class = tty_class;
dev->parent = device;
dev->release = tty_device_create_release;
dev_set_name(dev, "%s", name);
dev->groups = attr_grp;
dev_set_drvdata(dev, drvdata);
......
/* 注冊dev */
retval = device_register(dev);
if (retval)
goto err_put;
if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
......
/* 添加tty_driver的cdev圈匆,該函數(shù)在前面已經(jīng)出現(xiàn)過,這里的cdev是對應(yīng)新的tty_port */
retval = tty_cdev_add(driver, devt, index, 1);
if (retval)
goto err_del;
}
......
}
前面我們簡單的過了一遍 8250串口核心 和 8250串口硬件 的初始化流程捏雌。在初始化以后跃赚,數(shù)據(jù)結(jié)構(gòu)就如 串口tty驅(qū)動框架圖 所示。當(dāng)然以上內(nèi)容刪減了許多細(xì)節(jié)腹忽,有興趣的讀者可以閱讀源碼加深理解来累。
2.3.2 串口使用流程
相比于 初始化砚作,理解 串口讀取 的難度會更加高一些窘奏,以為讀寫過程會更加復(fù)雜嘹锁,希望筆者的一點(diǎn)筆墨可以幫助大家稍微理解一下其過程。
這里需要先知道着裹,串口讀取 一般是由 2 個(gè)線程來完成:
- 前臺線程:即用戶在應(yīng)用空間使用 read 等系統(tǒng)調(diào)用的線程领猾,該線程在進(jìn)入內(nèi)核空間會執(zhí)行相關(guān)的函數(shù)進(jìn)行等待。當(dāng)時(shí)機(jī)成熟時(shí)由 后臺線程 進(jìn)行喚醒并讀取相關(guān)數(shù)據(jù)到應(yīng)用恐案件骇扇。
- 后臺線程:由 串口驅(qū)動 在初始化時(shí)會定義一個(gè)線程摔竿,由該線程在中斷有數(shù)據(jù)時(shí)進(jìn)行讀寫。并把讀取到的數(shù)據(jù)存放在某個(gè)緩存中少孝,最后會喚醒 前臺線程 去讀取這個(gè)緩存中的數(shù)據(jù)继低。
在講解線程的執(zhí)行之前需要知道 中斷的初始化及其處理函數(shù),因?yàn)?串口讀取 與 中斷 密不可分稍走。下面會先講述 中斷初始化及其處理袁翁,然后再講述 讀取的前臺及后臺線程。
2.3.2.1 打開串口
串口中斷 的設(shè)置是在串口打開時(shí)設(shè)置的婿脸,所以我們直接查看串口打開時(shí)的調(diào)用圖譜及代碼即可知道相關(guān)內(nèi)容粱胜,如下所示:
tty_open(struct file_operations tty_fops)
/* 申請并初始化相關(guān)結(jié)構(gòu)體 */
->tty_open_by_driver
->tty_init_dev
->alloc_tty_struct
->tty_ldisc_init(設(shè)置默認(rèn)線路規(guī)程為N_TTY)
/* 執(zhí)行open操作,比如設(shè)置寄存器或者中斷等 */
->uart_open
->tty_port_open
->uart_port_activate
->uart_startup
->uart_port_startup
->serial8250_startup
->serial8250_do_startup
->setup_irq(univ8250_setup_irq)
->serial_link_irq_chain
->request_irq(serial8250_interrupt)
可以看到狐树,打開的流程很長焙压,其調(diào)用圖譜也非常的深,我們逐步看一下代碼吧
/* 在前面的章節(jié)中可以看到抑钟,tty_open是tty設(shè)備使用字符設(shè)備cdev對外的接口涯曲,當(dāng)我們打開 */
static int tty_open(struct inode *inode, struct file *filp)
{
struct tty_struct *tty;
int noctty, retval;
dev_t device = inode->i_rdev;
unsigned saved_flags = filp->f_flags;
......
/* 分配一個(gè)tty_file_private結(jié)構(gòu)體并賦值給文件的private_data成員 */
retval = tty_alloc_file(filp);
{
struct tty_file_private *priv;
priv = kmalloc(sizeof(*priv), GFP_KERNEL);
......
file->private_data = priv;
return 0;
}
......
/*
根據(jù)內(nèi)核注釋,tty_open_current_tty用于獲取當(dāng)前tty設(shè)備的鎖在塔,但從函數(shù)命名和返回值來看這應(yīng)該是另一種說法
根據(jù)筆者理解掀抹,這里應(yīng)該獲取當(dāng)前tty設(shè)備的tty_struct結(jié)構(gòu)體
如果獲取失敗則表明當(dāng)前的tty設(shè)備沒有打開,需要調(diào)用tty_open_by_driver重新打開
*/
tty = tty_open_current_tty(device, filp);
if (!tty)
tty = tty_open_by_driver(device, inode, filp);
......
/* 將當(dāng)前文件的私有數(shù)據(jù)即tty_file_private成員進(jìn)行填充心俗,再將其鏈入tty_stuct的tty_files鏈表 */
tty_add_file(tty, filp);
{
struct tty_file_private *priv = file->private_data;
priv->tty = tty;
priv->file = file;
spin_lock(&tty->files_lock);
list_add(&priv->list, &tty->tty_files);
spin_unlock(&tty->files_lock);
}
......
/* tty_struct結(jié)構(gòu)題的ops成員(sturct tty_operations)中的open函數(shù)傲武,即uart_open */
if (tty->ops->open)
retval = tty->ops->open(tty, filp);
else
retval = -ENODEV;
filp->f_flags = saved_flags;
......
}
static struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode,
struct file *filp)
{
struct tty_struct *tty;
struct tty_driver *driver = NULL;
int index = -1;
int retval;
mutex_lock(&tty_mutex);
/* 根據(jù)設(shè)備好找到對應(yīng)的tty_driver */
driver = tty_lookup_driver(device, filp, &index);
......
/* 檢查我們是否重復(fù)打開tty */
tty = tty_driver_lookup_tty(driver, filp, index);
......
/* 如果重復(fù)打開則執(zhí)行該分支 */
if (tty) {
if (tty_port_kopened(tty->port)) {
tty_kref_put(tty);
mutex_unlock(&tty_mutex);
tty = ERR_PTR(-EBUSY);
goto out;
}
mutex_unlock(&tty_mutex);
retval = tty_lock_interruptible(tty);
tty_kref_put(tty); /* drop kref from tty_driver_lookup_tty() */
if (retval) {
if (retval == -EINTR)
retval = -ERESTARTSYS;
tty = ERR_PTR(retval);
goto out;
}
retval = tty_reopen(tty);
if (retval < 0) {
tty_unlock(tty);
tty = ERR_PTR(retval);
}
} else {
/* 沒有重復(fù)打開則初始化出一個(gè)tty_struct */
tty = tty_init_dev(driver, index);
mutex_unlock(&tty_mutex);
}
......
return tty;
}
struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
{
struct tty_struct *tty;
int retval;
......
/* 分配tty_struct結(jié)構(gòu)體并初始化,該函數(shù)比較重要 */
......
tty_lock(tty);
/* 該函數(shù)主要講tty_struct的地址鏈接到tty_driver的成員中 */
retval = tty_driver_install_tty(driver, tty);
if (retval < 0)
goto err_free_tty;
/* 復(fù)制tty_struct中的tty_port成員 */
if (!tty->port)
tty->port = driver->ports[idx];
......
tty->port->itty = tty;
/* 調(diào)用線程規(guī)程并打開 */
retval = tty_ldisc_setup(tty, tty->link);
/* 以下為tty_ldisc_setup函數(shù)體 */
{
int retval = tty_ldisc_open(tty, tty->ldisc);
if (retval)
return retval;
if (o_tty) {
retval = tty_ldisc_open(o_tty, o_tty->ldisc);
if (retval) {
tty_ldisc_close(tty, tty->ldisc);
return retval;
}
}
return 0;
}
......
/* Return the tty locked so that it cannot vanish under the caller */
return tty;
......
}
/* 分配并初始化tty_struct */
struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
{
struct tty_struct *tty;
tty = kzalloc(sizeof(*tty), GFP_KERNEL);
if (!tty)
return NULL;
kref_init(&tty->kref);
tty->magic = TTY_MAGIC;
/* 初始化線路規(guī)程 */
tty_ldisc_init(tty);
/* 以下為tty_ldisc_init函數(shù)體 */
{
/* 線路規(guī)程初始化為N_TTY */
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
if (IS_ERR(ld))
panic("n_tty: init_tty");
tty->ldisc = ld;
}
tty->session = NULL;
tty->pgrp = NULL;
mutex_init(&tty->legacy_mutex);
mutex_init(&tty->throttle_mutex);
init_rwsem(&tty->termios_rwsem);
mutex_init(&tty->winsize_mutex);
init_ldsem(&tty->ldisc_sem);
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
INIT_WORK(&tty->hangup_work, do_tty_hangup);
mutex_init(&tty->atomic_write_lock);
spin_lock_init(&tty->ctrl_lock);
spin_lock_init(&tty->flow_lock);
spin_lock_init(&tty->files_lock);
INIT_LIST_HEAD(&tty->tty_files);
INIT_WORK(&tty->SAK_work, do_SAK_work);
tty->driver = driver;
tty->ops = driver->ops;
tty->index = idx;
tty_line_name(driver, idx, tty->name);
tty->dev = tty_get_device(tty);
return tty;
}
static int uart_open(struct tty_struct *tty, struct file *filp)
{
struct uart_driver *drv = tty->driver->driver_state;
int retval, line = tty->index;
struct uart_state *state = drv->state + line;
/* 設(shè)置tty_struct的driver_data成員為uart_state */
tty->driver_data = state;
/* 打開tty_port */
retval = tty_port_open(&state->port, tty, filp);
if (retval > 0)
retval = 0;
return retval;
}
int tty_port_open(struct tty_port *port, struct tty_struct *tty,
struct file *filp)
{
spin_lock_irq(&port->lock);
++port->count;
spin_unlock_irq(&port->lock);
tty_port_tty_set(port, tty);
/* 只有在硬件沒有初始化好的情況才能打開設(shè)備 */
mutex_lock(&port->mutex);
/* 檢測tty_port是否初始化過 */
if (!tty_port_initialized(port)) {
clear_bit(TTY_IO_ERROR, &tty->flags);
if (port->ops->activate) {
/* 執(zhí)行tty_port的activate方法城榛,即uart_port_ops中的uart_port_activate方法 */
int retval = port->ops->activate(port, tty);
if (retval) {
mutex_unlock(&port->mutex);
return retval;
}
}
tty_port_set_initialized(port, 1);
}
mutex_unlock(&port->mutex);
return tty_port_block_til_ready(port, tty, filp);
}
/* 使能uart_port */
static int uart_port_activate(struct tty_port *port, struct tty_struct *tty)
{
struct uart_state *state = container_of(port, struct uart_state, port);
struct uart_port *uport;
......
/* 啟動uart串口 */
return uart_startup(tty, state, 0);
}
/* 啟動串口 */
static int uart_startup(struct tty_struct *tty, struct uart_state *state,
int init_hw)
{
struct tty_port *port = &state->port;
int retval;
/* 檢查串口是否有初始化 */
if (tty_port_initialized(port))
return 0;
/* 執(zhí)行uart_port_startup */
retval = uart_port_startup(tty, state, init_hw);
......
return retval;
}
static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
int init_hw)
{
struct uart_port *uport = uart_port_check(state);
unsigned long page;
int retval = 0;
......
/* 確保已經(jīng)開辟了傳輸緩存 */
if (!state->xmit.buf) {
/* 如果沒有則獲取并賦值 */
page = get_zeroed_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
state->xmit.buf = (unsigned char *) page;
uart_circ_clear(&state->xmit);
}
/* 執(zhí)行uart_port的startup函數(shù)揪利,即serial8250_pops的serial8250_startup */
retval = uport->ops->startup(uport);
/* 以下為serial8250_startup 函數(shù)體 */
{
if (port->startup)
return port->startup(port);
return serial8250_do_startup(port);
}
if (retval == 0) {
......
/* 初始化串口的硬件設(shè)置 */
uart_change_speed(tty, state, NULL);
/* 設(shè)置串口的RTS和DTR信號 */
if (init_hw && C_BAUD(tty))
uart_port_dtr_rts(uport, 1);
}
......
return retval;
}
int serial8250_do_startup(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
unsigned long flags;
unsigned char lsr, iir;
int retval;
/* 省略的部分語句為根據(jù)端口類型設(shè)置硬件寄存器,不在本文的講述范圍內(nèi)狠持,有興趣的讀者可以閱讀源碼 */
......
/*
* For the Altera 16550 variants, set TX threshold trigger level.
*/
if (((port->type == PORT_ALTR_16550_F32) ||
(port->type == PORT_ALTR_16550_F64) ||
(port->type == PORT_ALTR_16550_F128)) && (port->fifosize > 1)) {
/* Bounds checking of TX threshold (valid 0 to fifosize-2) */
if ((up->tx_loadsz < 2) || (up->tx_loadsz > port->fifosize)) {
pr_err("ttyS%d TX FIFO Threshold errors, skipping\n",
serial_index(port));
} else {
serial_port_out(port, UART_ALTR_AFR,
UART_ALTR_EN_TXFIFO_LW);
serial_port_out(port, UART_ALTR_TX_LOW,
port->fifosize - up->tx_loadsz);
port->handle_irq = serial8250_tx_threshold_handle_irq;
}
}
if (port->irq && !(up->port.flags & UPF_NO_THRE_TEST)) {
unsigned char iir1;
/*
當(dāng)發(fā)送器是空閑并且中斷已經(jīng)被清除時(shí)疟位,需要測試UART不要讓它再次聲明發(fā)送中斷。
實(shí)際上當(dāng)發(fā)送器是空閑且允許中斷時(shí)喘垂,16500 應(yīng)該總是重新聲明該中斷甜刻。
為了讓改變寄存器生效绍撞,需要加入必要的延時(shí)。
*/
/* 關(guān)閉中斷 */
spin_lock_irqsave(&port->lock, flags);
if (up->port.irqflags & IRQF_SHARED)
disable_irq_nosync(port->irq);
/* 設(shè)置寄存器 */
wait_for_xmitr(up, UART_LSR_THRE);
serial_port_out_sync(port, UART_IER, UART_IER_THRI);
/* 延遲并等待寄存器生效 */
udelay(1);
/* 設(shè)置寄存器 */
iir1 = serial_port_in(port, UART_IIR);
serial_port_out(port, UART_IER, 0);
serial_port_out_sync(port, UART_IER, UART_IER_THRI);
/* 延遲并等待寄存器生效 */
udelay(1);
iir = serial_port_in(port, UART_IIR);
serial_port_out(port, UART_IER, 0);
/* 使能中斷 */
if (port->irqflags & IRQF_SHARED)
enable_irq(port->irq);
spin_unlock_irqrestore(&port->lock, flags);
/*
* If the interrupt is not reasserted, or we otherwise
* don't trust the iir, setup a timer to kick the UART
* on a regular basis.
*/
if ((!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) ||
up->port.flags & UPF_BUG_THRE) {
up->bugs |= UART_BUG_THRE;
}
}
/* 調(diào)用uart_8250_port結(jié)構(gòu)體中的setup_irq方法得院,即univ8250_driver_ops中的univ8250_setup_irq */
retval = up->ops->setup_irq(up);
if (retval)
goto out;
/* 初始化相關(guān)的硬件寄存器 */
......
}
/* 設(shè)置中斷 */
static int univ8250_setup_irq(struct uart_8250_port *up)
{
struct uart_port *port = &up->port;
int retval = 0;
......
if (!port->irq) {
} else
/* 如果設(shè)置了中斷號傻铣,則執(zhí)行serial_link_irq_chain */
retval = serial_link_irq_chain(up);
return retval;
}
static int serial_link_irq_chain(struct uart_8250_port *up)
{
struct hlist_head *h;
struct hlist_node *n;
struct irq_info *i;
int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
......
if (i->head) {
......
} else {
......
/* 申請中斷 */
ret = request_irq(up->port.irq, serial8250_interrupt,
irq_flags, up->port.name, i);
if (ret < 0)
serial_do_unlink(i, up);
}
return ret;
}
總結(jié)而言,open流程 可以分為 2個(gè)部分:
- 申請并初始化結(jié)構(gòu)體祥绞,比如 tty_struct
- 設(shè)置相關(guān)寄存器和初始化中斷
2.3.2.1 打開串口
上面講述了串口的 open流程非洲,完成了中斷設(shè)置和寄存器設(shè)置。此時(shí)我們的應(yīng)用線程開始讀取串口數(shù)據(jù)蜕径。那么讀取流程需要分開 2 個(gè)部分來講述:
- 中斷后臺進(jìn)程:負(fù)責(zé)從硬件讀取數(shù)據(jù)存放到 buffer 中两踏,并通過機(jī)制通知 用戶前臺進(jìn)程 獲取數(shù)據(jù)。
- 用戶前臺進(jìn)程:負(fù)責(zé)等待接收 buffer 中的數(shù)據(jù)
下面先講述 后臺中斷讀取兜喻,其調(diào)用圖譜如下:
/* 中斷設(shè)置梦染,該部分以前在前文中講過,不再贅述 */
serial8250_init
->serial8250_isa_init_ports(up->ops = &univ8250_driver_ops;)
/* 中斷處理函數(shù) */
serial8250_interrupt
->handle_irq(dw8250_handle_irq朴皆,是在dw8250_probe設(shè)置的)
->serial8250_handle_irq
->serial8250_rx_chars
|->serial8250_read_char//從硬件中讀取數(shù)據(jù)到tty_buffer中
| ->uart_insert_char
| ->tty_insert_flip_char
| ->__tty_insert_flip_char
| ->__tty_buffer_request_room
| ->tty_buffer_alloc
|->tty_flip_buffer_push//將tty_buffer中數(shù)據(jù)投放到線路規(guī)程層(ldsc)的緩沖區(qū)中
| ->tty_schedule_flip
| ->flush_to_ldisc
| ->receive_buf
| ->tty_port_default_receive_buf(tty_port)
| ->tty_ldisc_receive_buf
| ->n_tty_receive_buf
下面直接看中斷代碼:
static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
{
struct irq_info *i = dev_id;
struct list_head *l, *end = NULL;
int pass_counter = 0, handled = 0;
......
l = i->head;
/* 輪眉每個(gè)串口的中斷函數(shù) */
do {
struct uart_8250_port *up;
struct uart_port *port;
up = list_entry(l, struct uart_8250_port, list);
port = &up->port;
/* 執(zhí)行中斷函數(shù)帕识,也就是dw8250_handle_irq,該函數(shù)在dw8250_probe設(shè)置 */
if (port->handle_irq(port)) {
handled = 1;
end = NULL;
} else if (end == NULL)
end = l;
l = l->next;
......
} while (l != end);
......
return IRQ_RETVAL(handled);
}
static int dw8250_handle_irq(struct uart_port *p)
{
struct uart_8250_port *up = up_to_u8250p(p);
struct dw8250_data *d = p->private_data;
unsigned int iir = p->serial_in(p, UART_IIR);
unsigned int status;
unsigned long flags;
......
if (serial8250_handle_irq(p, iir))
return 1;
......
return 0;
}
int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
{
unsigned char status;
unsigned long flags;
struct uart_8250_port *up = up_to_u8250p(port);
......
if (status & (UART_LSR_DR | UART_LSR_BI)) {
if (!up->dma || handle_rx_dma(up, iir))
/* 從串口中讀取字節(jié) */
status = serial8250_rx_chars(up, status);
}
......
return 1;
}
unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr)
{
struct uart_port *port = &up->port;
int max_count = 256;
do {
/* 循環(huán)讀取字節(jié)到tty_buffer中车荔,該buffer位于uart_port數(shù)據(jù)結(jié)構(gòu)中 */
serial8250_read_char(up, lsr);
if (--max_count == 0)
break;
lsr = serial_in(up, UART_LSR);
} while (lsr & (UART_LSR_DR | UART_LSR_BI));
tty_flip_buffer_push(&port->state->port);
return lsr;
}
static void serial8250_read_char(struct uart_8250_port *up, unsigned char lsr)
{
struct uart_port *port = &up->port;
unsigned char ch;
char flag = TTY_NORMAL;
if (likely(lsr & UART_LSR_DR))
/* 讀取串口接收到的字節(jié) */
ch = serial_in(up, UART_RX);
......
/* 將數(shù)據(jù)放入tty_buffer中 */
uart_insert_char(port, lsr, UART_LSR_OE, ch, flag);
}
void uart_insert_char(struct uart_port *port, unsigned int status,
unsigned int overrun, unsigned int ch, unsigned int flag)
{
struct tty_port *tport = &port->state->port;
if ((status & port->ignore_status_mask & ~overrun) == 0)
/* 將字節(jié)ch保存到tty_buffer中 */
if (tty_insert_flip_char(tport, ch, flag) == 0)
++port->icount.buf_overrun;
if (status & ~port->ignore_status_mask & overrun)
/* 將字節(jié)ch保存到tty_buffer中 */
if (tty_insert_flip_char(tport, 0, TTY_OVERRUN) == 0)
++port->icount.buf_overrun;
}
static inline int tty_insert_flip_char(struct tty_port *port,
unsigned char ch, char flag)
{
/* 獲取tty_buffer的尾部 */
struct tty_buffer *tb = port->buf.tail;
int change;
......
if (!change && tb->used < tb->size) {
if (~tb->flags & TTYB_NORMAL)
*flag_buf_ptr(tb, tb->used) = flag;
/* 如果tty_buffer中還有空間渡冻,則將字節(jié)ch插入tty_buffer中尾部中并返回 */
*char_buf_ptr(tb, tb->used++) = ch;
return 1;
}
/* 如果沒有空間則執(zhí)行__tty_insert_flip_char */
return __tty_insert_flip_char(port, ch, flag);
}
int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag)
{
struct tty_buffer *tb;
int flags = (flag == TTY_NORMAL) ? TTYB_NORMAL : 0;
/* 開辟tty_buffer的空間 */
if (!__tty_buffer_request_room(port, 1, flags))
return 0;
/* 將字節(jié)ch插入剛剛開辟的tty_buffer中 */
tb = port->buf.tail;
if (~tb->flags & TTYB_NORMAL)
*flag_buf_ptr(tb, tb->used) = flag;
*char_buf_ptr(tb, tb->used++) = ch;
return 1;
}
static int __tty_buffer_request_room(struct tty_port *port, size_t size,
int flags)
{
struct tty_bufhead *buf = &port->buf;
struct tty_buffer *b, *n;
int left, change;
/* 獲取剩余空間 */
b = buf->tail;
if (b->flags & TTYB_NORMAL)
left = 2 * b->size - b->used;
else
left = b->size - b->used;
change = (b->flags & TTYB_NORMAL) && (~flags & TTYB_NORMAL);
if (change || left < size) {
/* 開辟tty_buffer的空間 */
n = tty_buffer_alloc(port, size);
if (n != NULL) {
n->flags = flags;
/* 設(shè)置緩沖區(qū)到尾部 */
buf->tail = n;
smp_store_release(&b->commit, b->used);
smp_store_release(&b->next, n);
} else if (change)
size = 0;
else
size = left;
}
return size;
}
static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size)
{
struct llist_node *free;
struct tty_buffer *p;
/* 對齊緩沖區(qū)大小 */
size = __ALIGN_MASK(size, TTYB_ALIGN_MASK);
if (size <= MIN_TTYB_SIZE) {
free = llist_del_first(&port->buf.free);
if (free) {
p = llist_entry(free, struct tty_buffer, free);
goto found;
}
}
......
/* 分配緩沖區(qū) */
p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC);
if (p == NULL)
return NULL;
found:
tty_buffer_reset(p, size);
atomic_add(size, &port->buf.mem_used);
return p;
}
到了這里,我們基本上就完成了 數(shù)據(jù)到tty_buffer 的填充忧便,接下來我們就需要使用 tty_flip_buffer_push 喚醒 前臺線程族吻,下面為代碼實(shí)例:
void tty_flip_buffer_push(struct tty_port *port)
{
tty_schedule_flip(port);
}
void tty_schedule_flip(struct tty_port *port)
{
struct tty_bufhead *buf = &port->buf;
/* paired w/ acquire in flush_to_ldisc(); ensures
* flush_to_ldisc() sees buffer data.
*/
smp_store_release(&buf->tail->commit, buf->tail->used);
/* 喚醒workqueue執(zhí)行處理,即調(diào)用flush_to_ldisc珠增,該函數(shù)在tty_buffer_init時(shí)初始化 */
queue_work(system_unbound_wq, &buf->work);
}
static void flush_to_ldisc(struct work_struct *work)
{
struct tty_port *port = container_of(work, struct tty_port, buf.work);
struct tty_bufhead *buf = &port->buf;
mutex_lock(&buf->lock);
while (1) {
struct tty_buffer *head = buf->head;
struct tty_buffer *next;
int count;
.....
/* 判斷是否有數(shù)據(jù)可讀 */
next = smp_load_acquire(&head->next);
count = smp_load_acquire(&head->commit) - head->read;
if (!count) {
if (next == NULL)
break;
buf->head = next;
tty_buffer_free(port, head);
continue;
}
/* 接收數(shù)據(jù) */
count = receive_buf(port, head, count);
if (!count)
break;
head->read += count;
}
mutex_unlock(&buf->lock);
}
static int receive_buf(struct tty_port *port, struct tty_buffer *head, int count)
{
/* 獲取tty_buffer的緩沖區(qū)地址 */
unsigned char *p = char_buf_ptr(head, head->read);
char *f = NULL;
if (~head->flags & TTYB_NORMAL)
f = flag_buf_ptr(head, head->read);
/*
這里執(zhí)行的是tty_port的client_ops操作集
其初始化是在函數(shù)tty_port_init中進(jìn)行初始化超歌,該回調(diào)的函數(shù)名為tty_port_default_receive_buf
*/
return port->client_ops->receive_buf(port, p, f, count);
}
static int tty_port_default_receive_buf(struct tty_port *port,
const unsigned char *p,
const unsigned char *f, size_t count)
{
int ret;
struct tty_struct *tty;
struct tty_ldisc *disc;
......
/* 使用線路規(guī)程的接收方法對數(shù)據(jù)進(jìn)行接收 */
ret = tty_ldisc_receive_buf(disc, p, (char *)f, count);
tty_ldisc_deref(disc);
return ret;
}
int tty_ldisc_receive_buf(struct tty_ldisc *ld, const unsigned char *p,
char *f, int count)
{
/*
使用線路規(guī)程的接收方法,前面講說初始化使用的是ntty作為默認(rèn)的線路規(guī)程
所以這里的回調(diào)receive_buf2為n_tty_receive_buf2函數(shù)蒂教,執(zhí)行該函數(shù)接收數(shù)據(jù)
請助理巍举,這里p是存放有接收數(shù)據(jù)的緩沖區(qū)
*/
if (ld->ops->receive_buf2)
count = ld->ops->receive_buf2(ld->tty, p, f, count);
else {
count = min_t(int, count, ld->tty->receive_room);
if (count && ld->ops->receive_buf)
ld->ops->receive_buf(ld->tty, p, f, count);
}
return count;
}
static int n_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
return n_tty_receive_buf_common(tty, cp, fp, count, 1);
}
static int n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count, int flow)
{
struct n_tty_data *ldata = tty->disc_data;
int room, n, rcvd = 0, overflow;
......
while (1) {
......
/* 在這里對數(shù)據(jù)進(jìn)行接收 */
/* ignore parity errors if handling overflow */
if (!overflow || !fp || *fp != TTY_PARITY)
__receive_buf(tty, cp, fp, n);
cp += n;
if (fp)
fp += n;
count -= n;
rcvd += n;
}
tty->receive_room = room;
/* Unthrottle if handling overflow on pty */
if (tty->driver->type == TTY_DRIVER_TYPE_PTY) {
if (overflow) {
tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE);
tty_unthrottle_safe(tty);
__tty_set_flow_change(tty, 0);
}
} else
n_tty_check_throttle(tty);
up_read(&tty->termios_rwsem);
return rcvd;
}
static void __receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
struct n_tty_data *ldata = tty->disc_data;
bool preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty));
/*
這里判斷分支比較多,需要根據(jù)不同的設(shè)置執(zhí)行不同的接收函數(shù)凝垛。
不同的接收函數(shù)對數(shù)據(jù)的處理不同懊悯,但在最終都會調(diào)用put_tty_queue來投放數(shù)據(jù)
下面以n_tty_receive_char_fast函數(shù)為例子進(jìn)行說明
*/
if (ldata->real_raw)
n_tty_receive_buf_real_raw(tty, cp, fp, count);
else if (ldata->raw || (L_EXTPROC(tty) && !preops))
n_tty_receive_buf_raw(tty, cp, fp, count);
else if (tty->closing && !L_EXTPROC(tty))
n_tty_receive_buf_closing(tty, cp, fp, count);
else {
if (ldata->lnext) {
char flag = TTY_NORMAL;
if (fp)
flag = *fp++;
n_tty_receive_char_lnext(tty, *cp++, flag);
count--;
}
if (!preops && !I_PARMRK(tty))
n_tty_receive_buf_fast(tty, cp, fp, count);
else
n_tty_receive_buf_standard(tty, cp, fp, count);
flush_echoes(tty);
/*
flush_chars回到調(diào)用的是uart_ops操作集的uart_flush_chars函數(shù)。
該函數(shù)使能串口發(fā)送應(yīng)用程的數(shù)據(jù)
*/
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}
if (ldata->icanon && !L_EXTPROC(tty))
return;
/* publish read_head to consumer */
smp_store_release(&ldata->commit_head, ldata->read_head);
/* 喚醒前臺進(jìn)程來接收數(shù)據(jù) */
if (read_cnt(ldata)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);
wake_up_interruptible_poll(&tty->read_wait, POLLIN);
}
}
static void
n_tty_receive_buf_fast(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
struct n_tty_data *ldata = tty->disc_data;
char flag = TTY_NORMAL;
/* 循環(huán)接收字節(jié) */
while (count--) {
if (fp)
flag = *fp++;
if (likely(flag == TTY_NORMAL)) {
/* 從緩沖區(qū)中獲取字節(jié) */
unsigned char c = *cp++;
if (!test_bit(c, ldata->char_map))
/* 將接收到的字節(jié)投放到線路規(guī)程的數(shù)據(jù)緩沖區(qū)中 */
n_tty_receive_char_fast(tty, c);
......
}
}
}
static inline void n_tty_receive_char_fast(struct tty_struct *tty, unsigned char c)
{
struct n_tty_data *ldata = tty->disc_data;
......
/* 投送字節(jié)到線路規(guī)程的數(shù)據(jù)中 */
put_tty_queue(c, ldata);
}
static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
{
/* 將字節(jié)賦值給線路規(guī)程中的read_buf中 */
*read_buf_addr(ldata, ldata->read_head) = c;
/* 以下為read_buf_addr函數(shù)體 */
{
return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
}
/* 記錄緩沖區(qū)當(dāng)前的頭部下標(biāo) */
ldata->read_head++;
}
到了這里梦皮,數(shù)據(jù)的 中斷后臺接收流程 就已經(jīng)完成了炭分。這里我們還需要注意 前臺接收進(jìn)程 的流程,求該流程比較簡單剑肯,相比于 后臺接收流程 來了說調(diào)用圖譜十分簡潔捧毛。其代碼實(shí)例和調(diào)用圖譜如下:
tty_read
->n_tty_read
->canon_copy_from_read_buf
static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
int i;
struct inode *inode = file_inode(file);
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
......
/* 調(diào)用線路規(guī)程的read方法,同理這里使用的是ntty作用默認(rèn)線路規(guī)程,所以其函數(shù)名為n_tty_read */
if (ld->ops->read)
i = ld->ops->read(tty, file, buf, count);
......
return i;
}
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
{
struct n_tty_data *ldata = tty->disc_data;
unsigned char __user *b = buf;
/* 聲明一個(gè)等待隊(duì)列實(shí)例 */
DEFINE_WAIT_FUNC(wait, woken_wake_function);
int c;
int minimum, time;
ssize_t retval = 0;
long timeout;
int packet;
size_t tail;
......
packet = tty->packet;
tail = ldata->read_tail;
/* 加入等待隊(duì)列 */
add_wait_queue(&tty->read_wait, &wait);
while (nr) {
/* First test for status change. */
if (packet && tty->link->ctrl_status) {
unsigned char cs;
if (b != buf)
break;
spin_lock_irq(&tty->link->ctrl_lock);
cs = tty->link->ctrl_status;
tty->link->ctrl_status = 0;
spin_unlock_irq(&tty->link->ctrl_lock);
if (put_user(cs, b)) {
retval = -EFAULT;
break;
}
b++;
nr--;
break;
}
if (!input_available_p(tty, 0)) {
up_read(&tty->termios_rwsem);
tty_buffer_flush_work(tty->port);
down_read(&tty->termios_rwsem);
if (!input_available_p(tty, 0)) {
......
/* 如果當(dāng)前由于某種原因不可讀呀忧,則進(jìn)程讓出當(dāng)前CPU师痕,等待喚醒 */
timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
timeout);
down_read(&tty->termios_rwsem);
continue;
}
}
if (ldata->icanon && !L_EXTPROC(tty)) {
......
} else {
int uncopied;
if (packet && b == buf) {
if (put_user(TIOCPKT_DATA, b)) {
retval = -EFAULT;
break;
}
b++;
nr--;
}
/* 根據(jù)不同情況將數(shù)據(jù)投放到應(yīng)用層 */
uncopied = copy_from_read_buf(tty, &b, &nr);
......
}
n_tty_check_unthrottle(tty);
if (b - buf >= minimum)
break;
if (time)
timeout = time;
}
if (tail != ldata->read_tail)
n_tty_kick_worker(tty);
up_read(&tty->termios_rwsem);
/* 移除出等待隊(duì)列 */
remove_wait_queue(&tty->read_wait, &wait);
mutex_unlock(&ldata->atomic_read_lock);
if (b - buf)
retval = b - buf;
return retval;
}
static int copy_from_read_buf(struct tty_struct *tty,
unsigned char __user **b,
size_t *nr)
{
struct n_tty_data *ldata = tty->disc_data;
int retval;
size_t n;
bool is_eof;
size_t head = smp_load_acquire(&ldata->commit_head);
size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
retval = 0;
n = min(head - ldata->read_tail, N_TTY_BUF_SIZE - tail);
n = min(*nr, n);
if (n) {
/* 獲取當(dāng)前數(shù)據(jù)包的所在地址 */
const unsigned char *from = read_buf_addr(ldata, tail);
/* 以下為read_buf_addr函數(shù)體 */
{
/* 獲取線路規(guī)程中的讀取buffer地址 */
return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)];
}
/* 將地址拷貝到應(yīng)用空間,其中b為應(yīng)用空間buffer地址而账,from為串口接收數(shù)據(jù)包所在地址 */
retval = copy_to_user(*b, from, n);
n -= retval;
......
*b += n;
*nr -= n;
}
return retval;
}
到了這里胰坟,串口讀流程 基本完成了。其 寫流程 框架類似福扬,但數(shù)據(jù)流相反腕铸,這里不做講述惜犀,有興趣的讀者可以自行閱讀源碼理解铛碑。
三、結(jié)語
本文更新得有些許慢虽界,主要是平時(shí)看代碼寫文章的時(shí)間少了汽烦,再加上 tty框架 復(fù)雜且代碼多,所以閱讀起來理解不是很容易莉御。按照筆者理解撇吞,包括筆者在內(nèi)大部分人一輩子都應(yīng)該不會自己去寫一個(gè) 串口或者tty驅(qū)動,但學(xué)習(xí) 串口和tty驅(qū)動 有助于理解和使用 線路規(guī)程礁叔,想 串口藍(lán)牙 等使用方法相信不少人會遇到過牍颈,學(xué)習(xí)串口驅(qū)動框架有助于我們工作生活中開發(fā)和調(diào)試。
本文與該系列的其他文章類似琅关,主要在于梳理驅(qū)動框架來幫助讀者理解煮岁,而非講述使用方法。所以對于很多不影響流程的細(xì)節(jié)都省略掉涣易,筆者建議讀者有條件的可以閱讀一遍代碼可以加深理解画机。如果本文有誤或者有講得不好的地方,請各位讀者指出并海涵新症。
四步氏、參考鏈接
http://www.wowotech.net/tty_framework/435.html
http://www.wowotech.net/sort/tty_framework
https://blog.csdn.net/lizuobin2/article/details/51773305/
https://blog.csdn.net/sharecode/article/details/9197567
https://blog.csdn.net/sharecode/article/details/9196591